home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / trn / part04 < prev    next >
Encoding:
Internet Message Format  |  1991-12-02  |  87.2 KB

  1. Subject:  v25i007:  trn 2.0 - threaded newsreader based on rn 4.4, Part04/13
  2. Newsgroups: comp.sources.unix
  3. Approved: vixie@pa.dec.com
  4.  
  5. Submitted-by: davison@borland.com (Wayne Davison)
  6. Posting-number: Volume 25, Issue 7
  7. Archive-name: trn/part04
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line, then unpack
  11. # it by saving it into a file and typing "sh file".  To overwrite existing
  12. # files, type "sh file -c".  You can also feed this as standard input via
  13. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  14. # will see the following message at the end:
  15. #        "End of archive 4 (of 13)."
  16. # Contents:  mt-write.c mthreads.8 ngstuff.c nntp/acttimes.c
  17. #   nntp/xthread.patch rthreads.c term.h uudecode.c
  18. # Wrapped by vixie@cognition.pa.dec.com on Tue Dec  3 16:34:52 1991
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f 'mt-write.c' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'mt-write.c'\"
  22. else
  23. echo shar: Extracting \"'mt-write.c'\" \(9609 characters\)
  24. sed "s/^X//" >'mt-write.c' <<'END_OF_FILE'
  25. X/* $Id: mt-write.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
  26. X**
  27. X** $Log: mt-write.c,v $
  28. X** Revision 4.4.3.1  1991/11/22  04:12:15  davison
  29. X** Trn Release 2.0
  30. X** 
  31. X*/
  32. X
  33. X#include "EXTERN.h"
  34. X#include "common.h"
  35. X#include "threads.h"
  36. X#include "mthreads.h"
  37. X
  38. static FILE *fp_out;
  39. static int seq;
  40. static int article_seq;
  41. X
  42. static int failure;
  43. X
  44. void write_subjects(), write_authors(), write_roots(), write_ids();
  45. void write_articles(), write_thread(), write_item();
  46. void enumerate_articles(), enumerate_thread();
  47. void free_leftovers();
  48. X
  49. X/* Write out all the data in a packed format that is easy for our newsreader
  50. X** to use.  We free things as we go, when we don't need them any longer.  If
  51. X** we encounter any write errors, the write_item routine sets a failure flag
  52. X** to halt our writing of the file, but we keep on plugging away to free
  53. X** everything up.
  54. X*/
  55. int
  56. write_data(filename)
  57. char *filename;
  58. X{
  59. X    if (filename == Nullch) {
  60. X    failure = 2;    /* A NULL filename indicates just free the data */
  61. X    } else if ((fp_out = fopen(filename, FOPEN_WB)) == Nullfp) {
  62. X    if (ensure_path(filename)) {
  63. X        if ((fp_out = fopen(filename, FOPEN_WB)) == Nullfp) {
  64. X        log_error("Unable to create file: `%s'.\n", filename);
  65. X        failure = 2;
  66. X        }
  67. X    } else {
  68. X        log_error("Unable to create path: `%s'.\n", filename);
  69. X        failure = 2;
  70. X    }
  71. X    } else {
  72. X    failure = 0;
  73. X#ifdef SETBUFFER
  74. X    setbuffer(fp_out, rwbuf, RWBUFSIZ);
  75. X#else
  76. X# ifdef SETVBUF
  77. X    setvbuf(fp_out, rwbuf, _IOFBF, RWBUFSIZ);
  78. X# endif
  79. X#endif
  80. X    }
  81. X
  82. X    /* If there's no roots, there's no data.  Leave the file with no length. */
  83. X    if (!total.root && !failure) {
  84. X    failure = -1;
  85. X    }
  86. X
  87. X    write_item(&total, sizeof (TOTAL));
  88. X
  89. X    enumerate_articles();
  90. X
  91. X    write_authors();
  92. X    write_subjects();
  93. X    write_roots();
  94. X    write_articles();
  95. X    write_ids();
  96. X    free_leftovers();
  97. X
  98. X    if (failure != 2) {
  99. X    fclose(fp_out);
  100. X    }
  101. X    if (failure == 1) {
  102. X    log_error("Write failed!  Removing `%s'.\n", filename);
  103. X    unlink(filename);
  104. X    }
  105. X    return failure <= 0;
  106. X}
  107. X
  108. X/* Recursively descend the article tree, enumerating the articles as we go.
  109. X** This way we can output the article sequence numbers into the data file.
  110. X*/
  111. void
  112. enumerate_articles()
  113. X{
  114. X    register ROOT *root;
  115. X
  116. X    seq = article_seq = 0;
  117. X
  118. X    for (root = root_root; root; root = root->link) {
  119. X    root->seq = seq++;
  120. X    if (!root->articles) {
  121. X        log_error("** No articles on this root??\n");
  122. X        continue;
  123. X    }
  124. X    enumerate_thread(root->articles);
  125. X    }
  126. X    if (seq != total.root) {
  127. X    log_error("** Wrote %d roots instead of %d **\n", seq, total.root);
  128. X    }
  129. X    if (article_seq != total.article) {
  130. X    log_error("** Wrote %d articles instead of %d **\n", article_seq, total.article);
  131. X    }
  132. X}
  133. X
  134. X/* Recursive routine for above-mentioned enumeration. */
  135. void
  136. enumerate_thread(article)
  137. ARTICLE *article;
  138. X{
  139. X    while (article) {
  140. X    article->seq = article_seq++;
  141. X    if (article->children) {
  142. X        enumerate_thread(article->children);
  143. X    }
  144. X    article = article->siblings;
  145. X    }
  146. X}
  147. X
  148. X#define write_and_free(str_ptr)    /* Comment for makedepend to     \
  149. X                    ** ignore the backslash above */ \
  150. X{\
  151. X    register int len = strlen(str_ptr) + 1;\
  152. X    write_item(str_ptr, len);\
  153. X    free(str_ptr);\
  154. X    string_offset += len;\
  155. X}
  156. X
  157. MEM_SIZE string_offset;
  158. X
  159. X/* Write out the author information:  first the use-counts, then the
  160. X** name strings all packed together.
  161. X*/
  162. void
  163. write_authors()
  164. X{
  165. X    register AUTHOR *author;
  166. X
  167. X    seq = 0;
  168. X    for (author = author_root; author; author = author->link) {
  169. X    write_item(&author->count, sizeof (WORD));
  170. X    author->seq = seq++;
  171. X    }
  172. X    if (seq != total.author) {
  173. X    log_error("** Wrote %d authors instead of %d **\n",
  174. X        seq, total.author);
  175. X    }
  176. X
  177. X    string_offset = 0;
  178. X
  179. X    for (author = author_root; author; author = author->link) {
  180. X    write_and_free(author->name);
  181. X    }
  182. X}
  183. X
  184. X/* Write out the subject information: first the packed string data, then
  185. X** the use-counts.  The order is important -- it is the order required
  186. X** by the roots for their subject structures.
  187. X*/
  188. void
  189. write_subjects()
  190. X{
  191. X    register ROOT *root;
  192. X    register SUBJECT *subject;
  193. X
  194. X    for (root = root_root; root; root = root->link) {
  195. X    for (subject = root->subjects; subject; subject = subject->link) {
  196. X        write_and_free(subject->str);
  197. X    }
  198. X    }
  199. X    if (string_offset != total.string1) {
  200. X    log_error("** Author/subject strings were %ld bytes instead of %ld **\n",
  201. X        string_offset, total.string1);
  202. X    }
  203. X
  204. X    seq = 0;
  205. X    for (root = root_root; root; root = root->link) {
  206. X    for (subject = root->subjects; subject; subject = subject->link) {
  207. X        write_item(&subject->count, sizeof (WORD));
  208. X        subject->seq = seq++;
  209. X    }
  210. X    }
  211. X    if (seq != total.subject) {
  212. X    log_error("** Wrote %d subjects instead of %d **\n",
  213. X        seq, total.subject);
  214. X    }
  215. X}
  216. X
  217. X/* Write the roots in a packed format.  Interpret the pointers into
  218. X** sequence numbers as we go.
  219. X*/
  220. void
  221. write_roots()
  222. X{
  223. X    register ROOT *root;
  224. X
  225. X    for (root = root_root; root; root = root->link) {
  226. X    p_root.articles = root->articles->seq;
  227. X    p_root.root_num = root->root_num;
  228. X    p_root.thread_cnt = root->thread_cnt;
  229. X    p_root.subject_cnt = root->subject_cnt;
  230. X    write_item(&p_root, sizeof (PACKED_ROOT));
  231. X    }
  232. X}
  233. X
  234. X#define rel_article(article, rseq)    ((article)? (article)->seq - (rseq) : 0)
  235. X#define valid_seq(ptr)        ((ptr)? (ptr)->seq : -1)
  236. X
  237. X/* Write all the articles in the same order that we sequenced them. */
  238. void
  239. write_articles()
  240. X{
  241. X    register ROOT *root;
  242. X
  243. X    for (root = root_root; root; root = root->link) {
  244. X    write_thread(root->articles);
  245. X    }
  246. X}
  247. X
  248. X/* Recursive routine to write the articles in thread order.  We depend on
  249. X** the fact that our first child is the very next article written (if we
  250. X** have children).
  251. X*/
  252. void
  253. write_thread(article)
  254. register ARTICLE *article;
  255. X{
  256. X    while (article) {
  257. X    p_article.num = article->num;
  258. X    p_article.date = article->date;
  259. X    p_article.subject = valid_seq(article->subject);
  260. X    p_article.author = valid_seq(article->author);
  261. X    p_article.flags = article->flags;
  262. X    p_article.child_cnt = article->child_cnt;
  263. X    p_article.parent = rel_article(article->parent, article->seq);
  264. X    p_article.siblings = rel_article(article->siblings, article->seq);
  265. X    p_article.root = article->root->seq;
  266. X    write_item(&p_article, sizeof (PACKED_ARTICLE));
  267. X    if (article->children) {
  268. X        write_thread(article->children);
  269. X    }
  270. X    article = article->siblings;
  271. X    }
  272. X}
  273. X
  274. WORD minus_one = -1;
  275. X
  276. X/* Write the message-id strings:  each domain name (not including the
  277. X** ".unknown." domain) followed by all of its associated unique ids.
  278. X** Then output the article sequence numbers they belong to.  This stuff
  279. X** is last because the newsreader doesn't need to read it.
  280. X*/
  281. void
  282. write_ids()
  283. X{
  284. X    register DOMAIN *domain;
  285. X    register ARTICLE *id;
  286. X    register DOMAIN *next_domain;
  287. X    register ARTICLE *next_id;
  288. X
  289. X    string_offset = 0;
  290. X
  291. X    for (domain = &unk_domain; domain; domain = domain->link) {
  292. X    if (domain != &unk_domain) {
  293. X        write_and_free(domain->name);
  294. X        if (!domain->ids) {
  295. X        log_error("** Empty domain name!! **\n");
  296. X        }
  297. X    }
  298. X    for (id = domain->ids; id; id = id->id_link) {
  299. X        write_and_free(id->id);
  300. X    }
  301. X    }
  302. X    if (string_offset != total.string2) {
  303. X    log_error("** Message-id strings were %ld bytes (%ld) **\n",
  304. X        string_offset, total.string2);
  305. X    }
  306. X    for (domain = &unk_domain; domain; domain = next_domain) {
  307. X    next_domain = domain->link;
  308. X    for (id = domain->ids; id; id = next_id) {
  309. X        next_id = id->id_link;
  310. X        write_item(&id->seq, sizeof (WORD));
  311. X        free(id);
  312. X    }
  313. X    write_item(&minus_one, sizeof (WORD));
  314. X    if (domain != &unk_domain) {
  315. X        free(domain);
  316. X    }
  317. X    }
  318. X    unk_domain.ids = Nullart;
  319. X    unk_domain.link = Null(DOMAIN*);
  320. X}
  321. X
  322. X/* Free everything that's left to free.
  323. X*/
  324. void
  325. free_leftovers()
  326. X{
  327. X    register ROOT *root, *next_root;
  328. X    register SUBJECT *subj, *next_subj;
  329. X    register AUTHOR *author, *next_author;
  330. X
  331. X    for (root = root_root; root; root = next_root) {
  332. X    next_root = root->link;
  333. X    for (subj = root->subjects; subj; subj = next_subj) {
  334. X        next_subj = subj->link;
  335. X        free(subj);
  336. X    }
  337. X    free(root);
  338. X    }
  339. X    for (author = author_root; author; author = next_author) {
  340. X    next_author = author->link;
  341. X    free(author);
  342. X    }
  343. X    root_root = Null(ROOT*);
  344. X    author_root = Null(AUTHOR*);
  345. X}
  346. X
  347. X/* This routine will check to be sure that the required path exists for
  348. X** the data file, and if not it will attempt to create it.
  349. X*/
  350. int
  351. ensure_path(filename)
  352. register char *filename;
  353. X{
  354. X    int status, pid, w;
  355. X    char tmpbuf[1024];
  356. X#ifdef MAKEDIR
  357. X    register char *cp, *last;
  358. X    register char *tbptr = tmpbuf+5;
  359. X
  360. X    if (!(last = rindex(filename, '/'))) {    /* find filename portion */
  361. X    return 1;                /* no path, we're fine */
  362. X    }
  363. X    *last = '\0';                /* truncate path at filename */
  364. X    strcpy(tmpbuf, "mkdir");
  365. X
  366. X    for (cp = last;;) {
  367. X    if (stat(filename, &filestat) >= 0 && (filestat.st_mode & S_IFDIR)) {
  368. X        *cp = '/';
  369. X        break;
  370. X    }
  371. X    if (!(cp = rindex(filename, '/'))) {/* find something that exists */
  372. X        break;
  373. X    }
  374. X    *cp = '\0';
  375. X    }
  376. X    
  377. X    for (cp = filename; cp <= last; cp++) {
  378. X    if (!*cp) {
  379. X        sprintf(tbptr, " %s", filename);
  380. X        tbptr += strlen(tbptr);        /* set up for mkdir call */
  381. X        *cp = '/';
  382. X    }
  383. X    }
  384. X    if (tbptr == tmpbuf+5) {
  385. X    return 1;
  386. X    }
  387. X#else
  388. X    sprintf(tmpbuf,"%s %s %d", file_exp(DIRMAKER), filename, 1);
  389. X#endif
  390. X
  391. X    if ((pid = vfork()) == 0) {
  392. X    execl(SH, SH, "-c", tmpbuf, Nullch);
  393. X    _exit(127);
  394. X    }
  395. X    while ((w = wait(&status)) != pid && w != -1) {
  396. X    ;
  397. X    }
  398. X    if (w == -1) {
  399. X    status = -1;
  400. X    }
  401. X    return !status;
  402. X}
  403. X
  404. X/* A simple routine to output some data only if we haven't failed any
  405. X** previous writes.
  406. X*/
  407. void
  408. write_item(buff, len)
  409. char *buff;
  410. int len;
  411. X{
  412. X    if (!failure) {
  413. X    if (fwrite(buff, 1, len, fp_out) < len) {
  414. X        failure = 1;
  415. X    }
  416. X    }
  417. X}
  418. END_OF_FILE
  419. if test 9609 -ne `wc -c <'mt-write.c'`; then
  420.     echo shar: \"'mt-write.c'\" unpacked with wrong size!
  421. fi
  422. # end of 'mt-write.c'
  423. fi
  424. if test -f 'mthreads.8' -a "${1}" != "-c" ; then 
  425.   echo shar: Will not clobber existing file \"'mthreads.8'\"
  426. else
  427. echo shar: Extracting \"'mthreads.8'\" \(10167 characters\)
  428. sed "s/^X//" >'mthreads.8' <<'END_OF_FILE'
  429. X.\" $Id: mthreads.8,v 4.4.3.1 1991/11/22 04:13:39 davison Trn $
  430. X.\" 
  431. X.de Sh
  432. X.br
  433. X.ne 5
  434. X.PP
  435. X\fB\\$1\fR
  436. X.PP
  437. X..
  438. X.de Sp
  439. X.if t .sp .5v
  440. X.if n .sp
  441. X..
  442. X.de Ip
  443. X.br
  444. X.ie \\n.$>=3 .ne \\$3
  445. X.el .ne 3
  446. X.IP "\\$1" \\$2
  447. X..
  448. X.\"
  449. X.\"     Set up \*(-- to give an unbreakable dash;
  450. X.\"
  451. X.tr \(bs-|\(bv\*(Tr
  452. X.ie n \{\
  453. X.ds -- \(bs-
  454. X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
  455. X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
  456. X.ds L" ""
  457. X.ds R" ""
  458. X.ds L' '
  459. X.ds R' '
  460. X'br\}
  461. X.el\{\
  462. X.ds -- \(em\|
  463. X.tr \*(Tr
  464. X.ds L" ``
  465. X.ds R" ''
  466. X.ds L' `
  467. X.ds R' '
  468. X'br\}
  469. X.TH MTHREADS 8 LOCAL
  470. X.UC 6
  471. X.SH NAME
  472. mthreads - threaded database manager for trn
  473. X.SH SYNOPSIS
  474. X.B mthreads [-d[MM]] [-e[HHMM]] [-acDfknstvz] [hierarchy_list]
  475. X.SH DESCRIPTION
  476. X.I Mthreads
  477. manages the thread files that are used by the
  478. X.IR trn (1)
  479. newsreader.
  480. X\*(L"Thread files\*(R" are used to store information about the news
  481. articles and how they are all related to one another.
  482. X.PP
  483. X.I Mthreads
  484. should be run periodically to update the thread database as new news arrives.
  485. It can be run in single-pass mode (out of cron), in daemon mode, or in a
  486. combination of the two.
  487. A site that gets its news feed during the night may just want to run mthreads
  488. once a day (trn will handle any local postings that show up between passes).
  489. If more processing is needed, either run mthreads
  490. more often or run it in daemon mode.
  491. In daemon mode, a background process is forked off that wakes up every 10
  492. minutes (by default) to check if the active file has been updated.
  493. When the mthreads daemon is sleeping between passes, it is possible
  494. to run an mthreads single pass.
  495. This is often useful if you wish to run an enhanced expire pass more than
  496. once a day (see the \-c and \-e options).
  497. X.SH INSTALLATION
  498. X.I Mthreads
  499. in installed in the RNLIB directory chosen during configuration.
  500. When it is run for the first time, it will automatically create a file called
  501. X.I active2
  502. in the MTLIB directory.
  503. This file is essentially a copy of the active file that keeps the newsgroup
  504. totals from the last run in one place.
  505. It is also used to choose which groups are to be processed into thread files.
  506. All groups start out as \*(L"unthreaded\*(R" unless they are turned on with
  507. a command like:
  508. X.IP
  509. mthreads all
  510. X.PP
  511. which would create thread file for all the groups.
  512. XFor testing purposes it is a good idea to start out small with a command
  513. like:
  514. X.IP
  515. mthreads news
  516. X.PP
  517. which would thread only the news hierarchy.
  518. Thread processing can be turned on or off for individual groups or entire
  519. hierarchies by specifying the groups in a syntax very similar to that used
  520. in the sys file.
  521. XFor example, to turn on all of soc and talk except for talk.politics, and
  522. to turn off news.lists, use the following command once:
  523. X.IP
  524. mthreads soc,talk,!talk.politics,!news.lists
  525. X.PP
  526. If mthreads complains that another mthreads process is already running,
  527. you can use the \-c option to tell it to continue trying to lock instead
  528. of giving up.
  529. X.PP
  530. Once all the desired groups are turned on, the hierarchy list should be
  531. omitted to allow mthreads to process all enabled groups.
  532. It can be used, however, in conjunction with the \-a option to customize
  533. which new groups get turned on as they are created.
  534. X.SH LOGGING
  535. As mthreads executes, some status information (including error messages) 
  536. is placed in
  537. the file mt.log in the MTLIB directory, unless you chose to use SYSLOG.
  538. This file will grow without bounds, and should be scanned periodically for
  539. errors, and trimmed in size when it grows too large.
  540. See the shell script
  541. X.I mt.check
  542. for an mt.log maintainer that will send mail if it finds database errors.
  543. X.SH OPTIONS
  544. X.TP 5
  545. X.B \-a
  546. is used to automatically turn on thread processing for new news groups as
  547. they are created.
  548. When this option is specified, the hierarchy list is used to limit
  549. which new groups get enabled (omitting the hierarchy list is the same
  550. as specifying \*(L"all\*(R").
  551. The default without \-a is to leave new groups unthreaded.
  552. X.TP 5
  553. X.B \-c
  554. will continue trying to lock the mthreads database for a single pass
  555. instead of giving up.
  556. This is useful for running special commands out of cron while an mthreads
  557. daemon is active.
  558. X.TP 5
  559. X.B \-D
  560. specifies debug processing.
  561. Any errors encountered reading a thread file will rename the offending
  562. file to \*(L"bad.read\*(R".
  563. Any errors detected while generating a new thread file will rename the
  564. file to \*(L"bad.write\*(R".
  565. If more than one 'D' is specified, each group's name is output into
  566. the log file before it is processed.
  567. X.TP 5
  568. X.B \-d
  569. is used to specify the daemon mode, where
  570. X.I mthreads
  571. forks a background task that periodically wakes up and checks for an updated
  572. active file.
  573. The number of minutes to wait after the completion of the last pass can
  574. be specified after the '-d' option (e.g. -d20), otherwise it will default to
  575. X10 minutes.
  576. X.TP 5
  577. X.B \-e
  578. tells
  579. X.I mthreads
  580. to run an enhanced expiration check on the database.
  581. Without this option, only articles below the minimum field in the active
  582. file are expired.
  583. With this option, mthreads will periodically list all the article numbers
  584. to see which ones actually exist.
  585. In single-pass mode the
  586. X.B -e
  587. option always affects the current pass \*(-- use it
  588. at lease once a day after expire has run.
  589. In daemon mode, the
  590. X.B -e
  591. option will cause one pass a day to be the enhanced expire pass.
  592. By default, this is the first time mthreads wakes up after 12:30 am.
  593. If a different time is desired, it can be specified in the form HHMM 
  594. X(e.g. -e2359).
  595. X.TP 5
  596. X.B -f
  597. is used to force
  598. X.I mthreads
  599. to open each and every thread file to see which ones really need to be
  600. updated, not just the ones that differ in the active/active2 comparison.
  601. It will also remove any extraneous thread files from unthreaded groups
  602. X(which should only occur if you manually change the active2 file).
  603. This option should only be used when manipulating the thread files in
  604. unorthodox ways.
  605. X.TP 5
  606. X.B -k
  607. can be used to terminate the currently running mthreads daemon, just as if it
  608. had received a terminate signal.
  609. When this option is specified, no other activity is performed.
  610. X.TP 5
  611. X.B -n
  612. tells
  613. X.I mthreads
  614. that no actual processing of thread files is to be performed.
  615. This can be used to just adjust which groups are enabled, without
  616. actually doing any of the processing right away.
  617. X.TP 5
  618. X.B -s<usec>
  619. tells mthreads to sleep for <usec> microseconds before processing each
  620. article.
  621. This is useful if your NNTP server cannot handle mthreads running at
  622. full speed.
  623. Using
  624. X.B -s
  625. by itself will sleep for an entire second to be compatible with older
  626. versions of mthreads.
  627. X.TP 5
  628. X.B -t
  629. is used to make mthreads update the active.times file (as specified
  630. during configuration) with new directory names as they are encountered.
  631. Don't use this option if your news software maintains this file for
  632. you (as C news does).
  633. X.TP 5
  634. X.B -v
  635. selects additional levels of verbosity in the log file.
  636. The default (without -v) is to log mthread's startup, the totals for each
  637. pass, and major database errors.
  638. Add one
  639. X.B -v
  640. to get extra reference line problems logged into the file.
  641. Add a second and a third for even more (useless?) information.
  642. A fourth will cause mthreads to output each group's name into the log file
  643. before it is processed.
  644. X.TP 5
  645. X.B -z
  646. tells mthreads to 'zap' any thread file it believes to be corrupt.
  647. This will allow the file to be regenerated from scratch on the next pass.
  648. X.TP 5
  649. X.B hierarchy_list
  650. The hierarchy list is used to turn thread processing on or off for the listed
  651. groups while limiting itself to updating only the listed groups.
  652. If specified with the \-a option, however, it only limits which new groups
  653. get enabled.
  654. The groups are specified in a manner very similar to the news software's
  655. sys file:  \*(L"news\*(R" matches all groups in news; \*(L"!news\*(R" excludes
  656. all groups in news; \*(L"comp.all.ibm.pc,!comp.all.ibm.pc.all\*(L" matches both
  657. comp.sys.ibm.pc and comp.binaries.ibm.pc, but not comp.binaries.ibm.pc.d.
  658. X.SH OUTPUT
  659. When
  660. X.I mthreads
  661. is run in single-pass mode it generates a stream a status characters on
  662. stdout that present a visual display of what is happening.  If 
  663. single-pass mode is used for regular processing, this output can be
  664. redirected to /dev/null.
  665. X.Sp
  666. The output definitions:
  667. X.br
  668. X    \&'.' = group's entry is up to date
  669. X.br
  670. X    \&':' = group processed \*(-- no change
  671. X.br
  672. X    \&'#' = group processed
  673. X.br
  674. X    \&'-' = group processed \*(-- is now empty
  675. X.br
  676. X    \&'x' = group excluded in active
  677. X.br
  678. X    \&'X' = group excluded in active2
  679. X.br
  680. X    \&'*' = chdir failed (might be ok)
  681. X.br
  682. X    \&'!' = write failed (is NOT ok)
  683. X.br
  684. X    \&'e' = informational error
  685. X.br
  686. X    \&'E' = database-affecting error
  687. X.SH CONFIGURATION
  688. During the configuration of
  689. X.IR trn ,
  690. a choice was made about where to place the thread data files.
  691. They either exist as a .thread file in each group's spool directory, or they
  692. are each a group.th file in a one-off directory structure on another drive.
  693. See the THREAD_DIR definition in config.h to review or change this definition.
  694. X.SH REBUILDING
  695. If the thread files are ever removed, also remove the file db.init in
  696. the THREAD_DIR.
  697. This file contains the byte-order of the machine that generated the database,
  698. and needs to be removed to truly start from scratch.
  699. An easy way to get
  700. X.I mthreads
  701. to remove all the files except for db.init is to specify the command:
  702. X.IP
  703. mthreads !all
  704. X.PP
  705. This also turns off thread processing for all groups.
  706. X.SH "ERROR HANDLING"
  707. If the active2 file is removed or corrupted, it will
  708. be automatically rebuilt in the normal course of operation.
  709. The record of which groups should be threaded will be lost, however.
  710. Missing/corrupted thread files are automatically re-built.
  711. X.SH EXAMPLES
  712. Recommended commands to run on a regular basis are:
  713. X.IP
  714. mthreads -dave0630
  715. X.PP
  716. to start up an mthreads daemon in verbose logging mode that automatically
  717. threads new groups and performs an extended expire at 6:30 am, or:
  718. X.IP
  719. mthreads -e >/dev/null
  720. X.PP
  721. to run an mthreads single-pass with extended expire that leaves new groups
  722. unthreaded.
  723. X.SH FILES
  724. X/usr/lib/news/active
  725. X.br
  726. X$MTLIB/active2
  727. X.br
  728. X$MTLIB/mt.log
  729. X.br
  730. X$THREAD_DIR/db.init
  731. X.br
  732. X$MTLIB/LOCKmthreads
  733. X.br
  734. Lots of thread data files.
  735. X.SH AUTHOR
  736. Wayne Davison <davison@borland.com>
  737. END_OF_FILE
  738. if test 10167 -ne `wc -c <'mthreads.8'`; then
  739.     echo shar: \"'mthreads.8'\" unpacked with wrong size!
  740. fi
  741. # end of 'mthreads.8'
  742. fi
  743. if test -f 'ngstuff.c' -a "${1}" != "-c" ; then 
  744.   echo shar: Will not clobber existing file \"'ngstuff.c'\"
  745. else
  746. echo shar: Extracting \"'ngstuff.c'\" \(11114 characters\)
  747. sed "s/^X//" >'ngstuff.c' <<'END_OF_FILE'
  748. X/* $Id: ngstuff.c,v 4.4 1991/09/09 20:23:31 sob Exp sob $
  749. X *
  750. X * $Log: ngstuff.c,v $
  751. X * Revision 4.4  1991/09/09  20:23:31  sob
  752. X * release 4.4
  753. X *
  754. X *
  755. X * 
  756. X */
  757. X/* This software is Copyright 1991 by Stan Barber. 
  758. X *
  759. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  760. X * use this software as long as: there is no monetary profit gained
  761. X * specifically from the use or reproduction of this software, it is not
  762. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  763. X * included prominently in any copy made. 
  764. X *
  765. X * The author make no claims as to the fitness or correctness of this software
  766. X * for any use whatsoever, and it is provided as is. Any use of this software
  767. X * is at the user's own risk. 
  768. X */
  769. X
  770. X#include "EXTERN.h"
  771. X#include "common.h"
  772. X#include "term.h"
  773. X#include "util.h"
  774. X#include "ng.h"
  775. X#include "bits.h"
  776. X#include "intrp.h"
  777. X#include "cheat.h"
  778. X#include "head.h"
  779. X#include "final.h"
  780. X#include "sw.h"
  781. X#ifdef USETHREADS
  782. X#include "threads.h"
  783. X#include "rthreads.h"
  784. X#include "rn.h"
  785. X#include "rcstuff.h"
  786. X#endif
  787. X#include "decode.h"
  788. X#include "INTERN.h"
  789. X#include "ngstuff.h"
  790. X
  791. void
  792. ngstuff_init()
  793. X{
  794. X    ;
  795. X}
  796. X
  797. X/* do a shell escape */
  798. X
  799. int
  800. escapade()
  801. X{
  802. X    register char *s;
  803. X    bool interactive = (buf[1] == FINISHCMD);
  804. X    bool docd;
  805. X    char whereiam[512];
  806. X
  807. X    if (!finish_command(interactive))    /* get remainder of command */
  808. X    return -1;
  809. X    s = buf+1;
  810. X    docd = *s != '!';
  811. X    if (!docd) {
  812. X    s++;
  813. X    }
  814. X    else {
  815. X    getwd(whereiam);
  816. X    if (chdir(cwd)) {
  817. X        printf(nocd,cwd) FLUSH;
  818. X        sig_catcher(0);
  819. X    }
  820. X    }
  821. X    while (*s == ' ') s++;
  822. X                    /* skip leading spaces */
  823. X    interp(cmd_buf, (sizeof cmd_buf), s);/* interpret any % escapes */
  824. X    resetty();                /* make sure tty is friendly */
  825. X    doshell(Nullch,cmd_buf);    /* invoke the shell */
  826. X    noecho();                /* and make terminal */
  827. X    crmode();                /*   unfriendly again */
  828. X    if (docd) {
  829. X    if (chdir(whereiam)) {
  830. X        printf(nocd,whereiam) FLUSH;
  831. X        sig_catcher(0);
  832. X    }
  833. X    }
  834. X#ifdef MAILCALL
  835. X    mailcount = 0;            /* force recheck */
  836. X#endif
  837. X    return 0;
  838. X}
  839. X
  840. X/* process & command */
  841. X
  842. int
  843. switcheroo()
  844. X{
  845. X    if (!finish_command(TRUE)) /* get rest of command */
  846. X    return -1;    /* if rubbed out, try something else */
  847. X    if (!buf[1])
  848. X    pr_switches();
  849. X#ifdef PUSHBACK
  850. X    else if (buf[1] == '&') {
  851. X    if (!buf[2]) {
  852. X        page_init();
  853. X        show_macros();
  854. X    }
  855. X    else {
  856. X        char tmpbuf[LBUFLEN];
  857. X        register char *s;
  858. X
  859. X        for (s=buf+2; isspace(*s); s++);
  860. X        mac_line(s,tmpbuf,(sizeof tmpbuf));
  861. X    }
  862. X    }
  863. X#endif
  864. X    else {
  865. X    bool docd = (instr(buf,"-d", TRUE) != Nullch);
  866. X     char whereami[512];
  867. X    if (docd)
  868. X        getwd(whereami);
  869. X    sw_list(buf+1);
  870. X    if (docd) {
  871. X        cwd_check();
  872. X        if (chdir(whereami)) {        /* -d does chdirs */
  873. X        printf(nocd,whereami) FLUSH;
  874. X        sig_catcher(0);
  875. X        }
  876. X    }
  877. X    }
  878. X    return 0;
  879. X}
  880. X
  881. X/* process range commands */
  882. X
  883. int
  884. numnum()
  885. X{
  886. X    ART_NUM min, max;
  887. X    char *cmdlst = Nullch;
  888. X    register char *s, *c;
  889. X    ART_NUM oldart = art;
  890. X    char tmpbuf[LBUFLEN];
  891. X    bool justone = TRUE;        /* assume only one article */
  892. X
  893. X    perform_cnt = 0;
  894. X    if (!finish_command(TRUE))    /* get rest of command */
  895. X    return NN_INP;
  896. X    if (lastart < 1) {
  897. X        fputs("\nNo articles\n",stdout) FLUSH;
  898. X        return NN_ASK;
  899. X    }
  900. X#ifdef ARTSRCH
  901. X    if (srchahead)
  902. X    srchahead = -1;
  903. X#endif
  904. X    for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++)
  905. X    if (!isdigit(*s))
  906. X        justone = FALSE;
  907. X    if (*s) {
  908. X    cmdlst = savestr(s);
  909. X    justone = FALSE;
  910. X    }
  911. X    else if (!justone)
  912. X    cmdlst = savestr("m");
  913. X    *s++ = ',';
  914. X    *s = '\0';
  915. X    safecpy(tmpbuf,buf,LBUFLEN);
  916. X    for (s = tmpbuf; c = index(s,','); s = ++c) {
  917. X    *c = '\0';
  918. X    if (*s == '.')
  919. X        min = oldart;
  920. X    else
  921. X        min = atol(s);
  922. X#ifdef USETHREADS
  923. X    if (min<absfirst && justone) {
  924. X        int r;
  925. X
  926. X        /* Check if this is a root number */
  927. X        for (r = 0; r < total.root; r++) {
  928. X        if (p_roots[r].root_num == min) {
  929. X            p_art = p_articles + p_roots[r].articles;
  930. X            art = p_art->num;
  931. X            if (p_art->subject == -1) {
  932. X            follow_thread('N');
  933. X            }
  934. X            return NN_REREAD;
  935. X        }
  936. X        }
  937. X    }
  938. X#endif
  939. X    if (min<absfirst) {        /* make sure it is reasonable */
  940. X        min = absfirst;
  941. X        printf("(First article is %ld)\n",(long)absfirst) FLUSH;
  942. X        pad(just_a_sec/3);
  943. X    }
  944. X    if ((s=index(s,'-')) != Nullch) {
  945. X        s++;
  946. X        if (*s == '$')
  947. X        max = lastart;
  948. X        else if (*s == '.')
  949. X        max = oldart;
  950. X        else
  951. X        max = atol(s);
  952. X    }
  953. X    else
  954. X        max = min;
  955. X    if (max>lastart) {
  956. X        max = lastart;
  957. X        if (min > max)
  958. X        min = max;
  959. X        printf("(Last article is %ld)\n",(long)lastart) FLUSH;
  960. X        pad(just_a_sec/3);
  961. X    }
  962. X    if (max < min) {
  963. X        fputs("\nBad range\n",stdout) FLUSH;
  964. X        if (cmdlst)
  965. X        free(cmdlst);
  966. X        return NN_ASK;
  967. X    }
  968. X    if (justone) {
  969. X        art = min;
  970. X        return NN_REREAD;
  971. X    }
  972. X    check_first(min);
  973. X    for (art=min; art<=max; art++) {
  974. X        if (perform(cmdlst,TRUE)) {
  975. X#ifdef VERBOSE
  976. X        IF(verbose)
  977. X            printf("\n(Interrupted at article %ld)\n",(long)art)
  978. X              FLUSH;
  979. X        ELSE
  980. X#endif
  981. X#ifdef TERSE
  982. X            printf("\n(Intr at %ld)\n",(long)art) FLUSH;
  983. X#endif
  984. X        if (cmdlst)
  985. X            free(cmdlst);
  986. X        return NN_ASK;
  987. X        }
  988. X    }
  989. X    }
  990. X    art = oldart;
  991. X    if (cmdlst)
  992. X    free(cmdlst);
  993. X    return NN_NORM;
  994. X}
  995. X
  996. X#ifdef USETHREADS
  997. int
  998. use_selected()
  999. X{
  1000. X    PACKED_ARTICLE *root_limit;
  1001. X    register char ch;
  1002. X    register int r;
  1003. X    char *cmdstr;
  1004. X    int ret = 1, orig_root_cnt = selected_root_cnt;
  1005. X
  1006. X    if (!finish_command(TRUE))    /* get rest of command */
  1007. X    return 0;
  1008. X    if (!(ch = buf[1]))
  1009. X    return -1;
  1010. X    cmdstr = savestr(buf+1);
  1011. X
  1012. X    perform_cnt = 0;
  1013. X    page_line = 1;
  1014. X
  1015. X    /* Multiple commands and commands that operate on individual articles
  1016. X    ** use the article loop.
  1017. X    */
  1018. X    if (strlen(cmdstr) > 1 || index("ejmMsSwW|=", ch)) {
  1019. X    bool want_unread = (unread_selector || ch == 'm');
  1020. X
  1021. X    for (r = 0; r < total.root; r++) {
  1022. X        if (scan_all_roots
  1023. X         || (!orig_root_cnt&&root_article_cnts[r]&&!(selected_roots[r]&4))
  1024. X         || (selected_roots[r] & (unread_selector+1))) {
  1025. X        p_art = p_articles + p_roots[r].articles;
  1026. X        root_limit = upper_limit( p_art, 0 );
  1027. X        for (; p_art < root_limit; p_art++) {
  1028. X            art = p_art->num;
  1029. X            if (p_art->subject != -1
  1030. X             && (!was_read(art) ^ want_unread)) {
  1031. X            if (perform(cmdstr, TRUE)) {
  1032. X                fputs("\nInterrupted\n", stdout) FLUSH;
  1033. X                goto break_out;
  1034. X            }
  1035. X            }
  1036. X            if (p_art == Nullart)
  1037. X            break;
  1038. X        }/* for all articles */
  1039. X        }/* if selected */
  1040. X    }/* for all threads */
  1041. X    }                /* other commands get the root loop */
  1042. X    else if (ch == '+' || ch == '-' || ch == 'J' || ch == 'T' || ch == 't') {
  1043. X    for (r = 0; r < total.root; r++) {
  1044. X        if (scan_all_roots
  1045. X         || (!orig_root_cnt&&root_article_cnts[r]&&!(selected_roots[r]&4))
  1046. X         || (selected_roots[r] & (unread_selector+1))) {
  1047. X        if (mode != 't' && ch != 't') {
  1048. X            printf("T%-5ld ", (long)p_roots[r].root_num);
  1049. X        }
  1050. X        p_art = p_articles + p_roots[r].articles;
  1051. X        art = p_art->num;
  1052. X        if (perform(cmdstr, FALSE)) {
  1053. X            fputs("\nInterrupted\n", stdout) FLUSH;
  1054. X            goto break_out;
  1055. X        }
  1056. X#ifdef VERBOSE
  1057. X        IF(verbose)
  1058. X            if (mode != 't' && ch != 't' && ch != 'T')
  1059. X            putchar('\n') FLUSH;
  1060. X#endif
  1061. X        }
  1062. X    }
  1063. X    }
  1064. X    else if (ch == 'E') {    /* one command needs no looping at all */
  1065. X    if (decode_fp) {
  1066. X        decode_end();
  1067. X    } else {
  1068. X        ret = 2;
  1069. X    }
  1070. X    }
  1071. X    else {
  1072. X    printf("???%s\n",cmdstr);
  1073. X    ret = -1;
  1074. X    }
  1075. X  break_out:
  1076. X    free(cmdstr);
  1077. X    return ret;
  1078. X}
  1079. X#endif
  1080. X
  1081. int
  1082. perform(cmdlst,toplevel)
  1083. register char *cmdlst;
  1084. int toplevel;
  1085. X{
  1086. X    register int ch;
  1087. X    
  1088. X    if (toplevel) {
  1089. X    printf("%-6ld ",art);
  1090. X    fflush(stdout);
  1091. X    }
  1092. X    perform_cnt++;
  1093. X    for (; ch = *cmdlst; cmdlst++) {
  1094. X    if (isspace(ch) || ch == ':')
  1095. X        continue;
  1096. X    if (ch == 'j') {
  1097. X        if (!was_read(art)) {
  1098. X        mark_as_read();
  1099. X#ifdef VERBOSE
  1100. X        IF(verbose)
  1101. X            fputs("\tJunked",stdout);
  1102. X#endif
  1103. X        }
  1104. X#ifdef USETHREADS
  1105. X        else if (unread_selector)
  1106. X        goto unselect_it;
  1107. X#endif
  1108. X    }
  1109. X#ifdef USETHREADS
  1110. X    else if (ch == '+') {
  1111. X      register char mask = unread_selector+1;
  1112. X        find_article(art);
  1113. X        if (p_art && !(selected_roots[p_art->root] & mask)) {
  1114. X          register int r = p_art->root;
  1115. X        selected_roots[r] |= mask;
  1116. X        selected_root_cnt++;
  1117. X        if (mode == 't') {
  1118. X            selected_count += root_article_cnts[r];
  1119. X        } else {
  1120. X            selected_count += count_one_root(r);
  1121. X#ifdef VERBOSE
  1122. X            IF(verbose)
  1123. X            fputs("\tSelected",stdout);
  1124. X#endif
  1125. X        }
  1126. X        }
  1127. X    }
  1128. X    else if (ch == '-') {
  1129. X      register char mask;
  1130. X      unselect_it:
  1131. X        mask = unread_selector+1;
  1132. X        find_article(art);
  1133. X        if (p_art && selected_root_cnt
  1134. X         && (selected_roots[p_art->root] & mask)) {
  1135. X          register int r = p_art->root;
  1136. X        selected_roots[r] &= ~mask;
  1137. X        if (unread_selector)
  1138. X            selected_roots[r] |= 4;
  1139. X        selected_root_cnt--;
  1140. X        if (mode == 't') {
  1141. X            selected_count -= root_article_cnts[r];
  1142. X        } else {
  1143. X            selected_count -= count_one_root(r);
  1144. X#ifdef VERBOSE
  1145. X            IF(verbose)
  1146. X            fputs("\tDeselected",stdout);
  1147. X#endif
  1148. X        }
  1149. X        }
  1150. X    }
  1151. X    else if (ch == 't') {
  1152. X        find_article(art);
  1153. X        entire_tree();
  1154. X    }
  1155. X    else if (ch == 'J' || ch == 'T' || ch == ',') {
  1156. X        char tmpbuf[128];
  1157. X        ART_NUM oldart = art;
  1158. X
  1159. X        find_article(art);
  1160. X        if (ch == ',')
  1161. X        mark_as_read();
  1162. X        if (p_art) {
  1163. X        if (ch == 'T') {
  1164. X            sprintf(tmpbuf,"T%ld\t# %s",
  1165. X            (long)p_roots[p_art->root].root_num,
  1166. X            subject_ptrs[p_art->subject]);
  1167. X            fputs(tmpbuf,stdout);
  1168. X            kf_append(tmpbuf);
  1169. X        }
  1170. X        follow_thread(ch == ',' ? 'K' : 'J');
  1171. X        art = oldart;
  1172. X        }
  1173. X    }
  1174. X#endif
  1175. X    else if (ch == 'm') {
  1176. X        if (was_read(art)) {
  1177. X        unmark_as_read();
  1178. X#ifdef VERBOSE
  1179. X        IF(verbose)
  1180. X            fputs("\tMarked unread",stdout);
  1181. X#endif
  1182. X        }
  1183. X    }
  1184. X    else if (ch == 'M') {
  1185. X#ifdef DELAYMARK
  1186. X        delay_unmark(art);
  1187. X#ifdef VERBOSE
  1188. X        IF(verbose)
  1189. X        fputs("\tWill return",stdout);
  1190. X#endif
  1191. X#else
  1192. X        notincl("M");
  1193. X        return -1;
  1194. X#endif
  1195. X    }
  1196. X    else if (ch == '=') {
  1197. X        printf("\t%s",fetchsubj(art,FALSE,FALSE));
  1198. X#ifdef VERBOSE
  1199. X        IF(verbose)
  1200. X        ;
  1201. X        ELSE
  1202. X#endif
  1203. X        putchar('\n') FLUSH;        /* ghad! */
  1204. X    }
  1205. X    else if (ch == 'C') {
  1206. X#ifdef ASYNC_PARSE
  1207. X        printf("\t%sancelled",(cancel_article() ? "Not c" : "C"));
  1208. X#else
  1209. X        notincl("C");
  1210. X        return -1;
  1211. X#endif
  1212. X    }
  1213. X    else if (ch == '%') {
  1214. X#ifdef ASYNC_PARSE
  1215. X        char tmpbuf[512];
  1216. X
  1217. X        if (one_command)
  1218. X        interp(tmpbuf, (sizeof tmpbuf), cmdlst);
  1219. X        else
  1220. X        cmdlst = dointerp(tmpbuf, (sizeof tmpbuf), cmdlst, ":") - 1;
  1221. X        perform_cnt--;
  1222. X        if (perform(tmpbuf,FALSE))
  1223. X        return -1;
  1224. X#else
  1225. X        notincl("%");
  1226. X        return -1;
  1227. X#endif
  1228. X    }
  1229. X    else if (index("!&sSwWe|",ch)) {
  1230. X        if (one_command)
  1231. X        strcpy(buf,cmdlst);
  1232. X        else
  1233. X        cmdlst = cpytill(buf,cmdlst,':') - 1;
  1234. X        /* we now have the command in buf */
  1235. X        if (ch == '!') {
  1236. X        escapade();
  1237. X#ifdef VERBOSE
  1238. X        IF(verbose)
  1239. X            fputs("\tShell escaped",stdout);
  1240. X#endif
  1241. X        }
  1242. X        else if (ch == '&') {
  1243. X        switcheroo();
  1244. X#ifdef VERBOSE
  1245. X        IF(verbose)
  1246. X            if (buf[1] && buf[1] != '&')
  1247. X            fputs("\tSwitched",stdout);
  1248. X#endif
  1249. X        }
  1250. X        else {
  1251. X        putchar('\t');
  1252. X        save_article();
  1253. X#ifdef VERBOSE
  1254. X        IF(verbose)
  1255. X            ;
  1256. X        ELSE
  1257. X#endif
  1258. X            putchar('\n') FLUSH;
  1259. X        }
  1260. X    }
  1261. X    else {
  1262. X        printf("\t???%s\n",cmdlst);
  1263. X        return -1;
  1264. X    }
  1265. X#ifdef VERBOSE
  1266. X    fflush(stdout);
  1267. X#endif
  1268. X    if (one_command)
  1269. X        break;
  1270. X    }
  1271. X    if (toplevel) {
  1272. X#ifdef VERBOSE
  1273. X    IF(verbose)
  1274. X        putchar('\n') FLUSH;
  1275. X#endif
  1276. X    }
  1277. X    if( int_count ) {
  1278. X    int_count = 0;
  1279. X    return -1;
  1280. X    }
  1281. X    return 0;
  1282. X}
  1283. END_OF_FILE
  1284. if test 11114 -ne `wc -c <'ngstuff.c'`; then
  1285.     echo shar: \"'ngstuff.c'\" unpacked with wrong size!
  1286. fi
  1287. # end of 'ngstuff.c'
  1288. fi
  1289. if test -f 'nntp/acttimes.c' -a "${1}" != "-c" ; then 
  1290.   echo shar: Will not clobber existing file \"'nntp/acttimes.c'\"
  1291. else
  1292. echo shar: Extracting \"'nntp/acttimes.c'\" \(10481 characters\)
  1293. sed "s/^X//" >'nntp/acttimes.c' <<'END_OF_FILE'
  1294. X/* $Header: acttimes.c,v 1.1 91/11/02 18:38:29 davison Exp $
  1295. X**
  1296. X** $Log:    acttimes.c,v $
  1297. X*/
  1298. X
  1299. X/* This program will maintain the file active.times if your news software
  1300. X** doesn't already do this for you.  The file contains a list of newsgroup
  1301. X** names followed by the time of creation (in seconds since 1970) and the
  1302. X** address of the creator.  Since we can't tell who actually created the
  1303. X** group, they will all indicate acttimes@DOMAIN.
  1304. X**
  1305. X** Place this file in your NNTP support directory and change your
  1306. X** common/conf.h to undef NGDATE_FILE and STAT_FILE, and define
  1307. X** ACTIVE_TIMES_FILE.  To make, you can replace every mention of
  1308. X** "mkgrdates" in the support/Makefile with "acttimes" and then
  1309. X** type "make".  
  1310. X**
  1311. X** To use this without having NNTP around, undefine the NNTP_SUPPORT
  1312. X** define, edit the other defines that follow to indicate your setup,
  1313. X** and compile it with something like "cc -O -o acttimes acttimes.c".
  1314. X**
  1315. X** Use either "acttimes -d" to start a daemon process that wakes up every
  1316. X** 10 minutes (by default) to check if the active file is a different
  1317. X** size, or put "acttimes" into your cron file to be run periodically.
  1318. X*/
  1319. X
  1320. X#define NNTP_SUPPORT        /* comment out if not using NNTP */
  1321. X
  1322. X#include <stdio.h>
  1323. X#include <sys/types.h>
  1324. X#include <sys/stat.h>
  1325. X#include <signal.h>
  1326. X#include <errno.h>
  1327. X#ifdef NNTP_SUPPORT
  1328. X#include "../common/conf.h"
  1329. X#endif
  1330. X#ifdef USG
  1331. X#include <time.h>
  1332. X#else
  1333. X#include <sys/time.h>
  1334. X#endif
  1335. X
  1336. X/* ---------- Start of configuration defines ---------- */
  1337. X
  1338. X#ifndef USG
  1339. X#define TERMIO            /* Is this is termio system? */
  1340. X#endif
  1341. X
  1342. X/* NNTP sites have the following already defined in ../common/conf.h */
  1343. X
  1344. X#ifndef DOMAIN            /* our domain name */
  1345. X#define DOMAIN    "local"
  1346. X#endif
  1347. X
  1348. X#ifndef ACTIVE_FILE        /* the active file for your news system */
  1349. X#define ACTIVE_FILE "/usr/lib/news/active"
  1350. X#endif
  1351. X
  1352. X#ifndef ACTIVE_TIMES_FILE    /* the name of the file to update */
  1353. X#define ACTIVE_TIMES_FILE "/usr/lib/news/active.times"
  1354. X#endif
  1355. X
  1356. X#ifndef SIGRET            /* set this to "int" if you have problems */
  1357. X#define SIGRET void
  1358. X#endif
  1359. X
  1360. X#ifndef MAXPATHLEN        /* you'll probably want to leave this alone */
  1361. X#define    MAXPATHLEN    1024
  1362. X#endif
  1363. X
  1364. X/*#define index   strchr    /* uncomment these if you need them */
  1365. X/*#define rindex  strrchr    /* (i.e. if index is undefined) */
  1366. X
  1367. X/* ---------- End of configuration defines ---------- */
  1368. X
  1369. X#ifdef TERMIO
  1370. X#include <termio.h>
  1371. X#else
  1372. X#include <sgtty.h>
  1373. X#endif
  1374. X
  1375. X#define TIMER_FIRST 1
  1376. X#define TIMER_DEFAULT (10 * 60)
  1377. X
  1378. X#define strnEQ(x,y,n) (!strncmp((x),(y),(n)))
  1379. X
  1380. extern errno;
  1381. X
  1382. char *index(), *rindex(), *malloc();
  1383. SIGRET alarm_handler(), quit_handler();
  1384. void active_times(), free_lines(), wrap_it_up();
  1385. X
  1386. typedef struct _active_line {
  1387. X    struct _active_line *link;
  1388. X    char *name;
  1389. X    char type;
  1390. X} ACTIVE_LINE;
  1391. X
  1392. ACTIVE_LINE *line_root = NULL, *last_line = NULL, *pline = NULL;
  1393. long last_actsize;
  1394. int daemon_delay = 0, kill_daemon = 0, old_groups = 0;
  1395. X
  1396. XFILE *fp_lock;
  1397. X
  1398. struct stat filestat;
  1399. X
  1400. char buf[MAXPATHLEN];
  1401. char lockfile[MAXPATHLEN];
  1402. X
  1403. main(argc, argv)
  1404. int argc;
  1405. char *argv[];
  1406. X{
  1407. X    int fd;
  1408. X    long pid;
  1409. X    char *cp;
  1410. X
  1411. X    while (--argc) {
  1412. X    if (**++argv == '-') {
  1413. X        while (*++*argv) {
  1414. X        switch (**argv) {
  1415. X        case 'd':        /* run in daemon mode */
  1416. X            if (*++*argv <= '9' && **argv >= '0') {
  1417. X            daemon_delay = atoi(*argv) * 60;
  1418. X            while (*++*argv <= '9' && **argv >= '0') {
  1419. X                ;
  1420. X            }
  1421. X            } else {
  1422. X            daemon_delay = TIMER_DEFAULT;
  1423. X            }
  1424. X            --*argv;
  1425. X            break;
  1426. X        case 'k':        /* kill running acttimes */
  1427. X            kill_daemon++;
  1428. X            break;
  1429. X        default:
  1430. X            fprintf(stderr, "Unknown option: '%c'\n", **argv);
  1431. X            exit(1);
  1432. X        }
  1433. X        }
  1434. X    } else {
  1435. X        fprintf(stderr,
  1436. X        "Usage:  acttimes [-d<mins>]\nOr:     acttimes -k\n");
  1437. X        exit(1);
  1438. X    }
  1439. X    }
  1440. X
  1441. X    /* Set up a nice friendly umask. */
  1442. X    umask(002);
  1443. X
  1444. X    /* Make sure we're not already running by creating a lock file. */
  1445. X    strcpy(lockfile, ACTIVE_TIMES_FILE);
  1446. X    if ((cp = rindex(lockfile, '/')) != 0) {
  1447. X    cp++;
  1448. X    } else {
  1449. X    cp = lockfile;
  1450. X    }
  1451. X    *cp = '\0';
  1452. X    sprintf(buf, "%sLOCK.%ld", lockfile, (long)getpid());
  1453. X    if ((fp_lock = fopen(buf, "w")) == 0) {
  1454. X    fprintf(stderr, "Unable to create lock temporary `%s'.\n", buf);
  1455. X    exit(1);
  1456. X    }
  1457. X    fprintf(fp_lock, "%ld\n", (long)getpid());
  1458. X    fclose(fp_lock);
  1459. X
  1460. X    /* Try to link to lock file. */
  1461. X    strcat(lockfile, "LOCKacttimes");
  1462. X  dolink:
  1463. X    if (link(buf, lockfile) < 0) {
  1464. X      long otherpid;
  1465. X    /* Try to avoid possible race with daemon starting up. */
  1466. X    sleep (5);
  1467. X    if ((fp_lock = fopen(lockfile, "r")) == 0) {
  1468. X        fprintf(stderr, "unable to open %s\n", lockfile);
  1469. X        unlink(buf);
  1470. X        exit(1);
  1471. X    }
  1472. X    if (fscanf(fp_lock, "%ld", &otherpid) != 1) { 
  1473. X        fprintf(stderr, "unable to read pid from %s\n", lockfile);
  1474. X        unlink(buf);
  1475. X        fclose(fp_lock);
  1476. X        exit(1);
  1477. X    }
  1478. X    fclose(fp_lock);
  1479. X    if (kill(otherpid, kill_daemon ? SIGTERM : 0) == -1
  1480. X     && errno == ESRCH) {
  1481. X        if (unlink(lockfile) == -1) {
  1482. X        fprintf(stderr, "unable to unlink lockfile %s\n", lockfile);
  1483. X        unlink(buf);
  1484. X        exit(1);
  1485. X        }
  1486. X        if (!kill_daemon) {
  1487. X        goto dolink;
  1488. X        }
  1489. X    }
  1490. X    unlink(buf);
  1491. X    if (kill_daemon) {
  1492. X        fprintf(stderr, "killing currently running acttimes.\n");
  1493. X        exit(0);
  1494. X    } else {
  1495. X        fprintf(stderr, "acttimes is already running.\n");
  1496. X        exit(1);
  1497. X    }
  1498. X    }
  1499. X
  1500. X    unlink(buf);            /* remove temporary LOCK.<pid> file */
  1501. X
  1502. X    if (kill_daemon) {
  1503. X    fprintf(stderr, "acttimes is not running.\n");
  1504. X    wrap_it_up(1);
  1505. X    }
  1506. X
  1507. X#ifdef SIGHUP
  1508. X    if( signal( SIGHUP, SIG_IGN ) != SIG_IGN ) {
  1509. X    signal( SIGHUP, quit_handler );
  1510. X    }
  1511. X#endif
  1512. X    if( signal( SIGINT, SIG_IGN ) != SIG_IGN ) {
  1513. X    signal( SIGINT, quit_handler );
  1514. X    }
  1515. X#ifdef SIGQUIT
  1516. X    if( signal( SIGQUIT, SIG_IGN ) != SIG_IGN ) {
  1517. X    signal( SIGQUIT, quit_handler );
  1518. X    }
  1519. X#endif
  1520. X    signal( SIGTERM, quit_handler );
  1521. X#ifdef SIGTTIN
  1522. X    signal( SIGTTIN, SIG_IGN );
  1523. X    signal( SIGTTOU, SIG_IGN );
  1524. X#endif
  1525. X    signal( SIGALRM, SIG_IGN );
  1526. X
  1527. X    /* If we're not in daemon mode, run through once and quit. */
  1528. X    if (!daemon_delay) {
  1529. X    active_times();
  1530. X    } else {
  1531. X    /* For daemon mode, we cut ourself off from anything tty-related and
  1532. X    ** run in the background (involves forks, but no knives).
  1533. X    */
  1534. X    close(0);
  1535. X    if (open("/dev/null", 2) != 0) {
  1536. X        fprintf(stderr, "unable to open /dev/null!\n");
  1537. X        wrap_it_up(1);
  1538. X    }
  1539. X    close(1);
  1540. X    close(2);
  1541. X    dup(0);
  1542. X    dup(0);
  1543. X    while ((pid = fork()) < 0) {
  1544. X        sleep(2);
  1545. X    }
  1546. X    if (pid) {
  1547. X        exit(0);
  1548. X    }
  1549. X#ifdef TIOCNOTTY
  1550. X    if ((fd = open("/dev/tty", 1)) >= 0) {
  1551. X        ioctl(fd, TIOCNOTTY, (int*)0);
  1552. X        close(fd);
  1553. X    }
  1554. X#else
  1555. X    (void) setpgrp();
  1556. X    while ((pid = fork()) < 0) {
  1557. X        sleep(2);
  1558. X    }
  1559. X    if (pid) {
  1560. X        exit(0);
  1561. X    }
  1562. X#endif
  1563. X    /* Put our pid in the lock file for death detection */
  1564. X    if( (fp_lock = fopen(lockfile, "w")) != 0) {
  1565. X        fprintf(fp_lock, "%ld\n", (long)getpid());
  1566. X        fclose(fp_lock);
  1567. X    }
  1568. X
  1569. X    signal(SIGALRM, alarm_handler);
  1570. X
  1571. X    /* Start timer -- first interval is shorter than all others */
  1572. X    alarm(TIMER_FIRST);
  1573. X    for (;;) {
  1574. X        pause();        /* let alarm go off */
  1575. X        alarm(0);
  1576. X
  1577. X        if (stat(ACTIVE_FILE, &filestat) < 0) {
  1578. X        fprintf(stderr, "Unable to stat active file -- quitting.\n");
  1579. X        wrap_it_up(1);
  1580. X        }
  1581. X        if (filestat.st_size != last_actsize) {
  1582. X        last_actsize = filestat.st_size;
  1583. X        active_times();
  1584. X        }
  1585. X        alarm(daemon_delay);
  1586. X    } /* for */
  1587. X    }/* if */
  1588. X
  1589. X    wrap_it_up(0);
  1590. X}
  1591. X
  1592. SIGRET
  1593. alarm_handler()
  1594. X{
  1595. X    signal(SIGALRM, alarm_handler);
  1596. X}
  1597. X
  1598. SIGRET
  1599. quit_handler()
  1600. X{
  1601. X    wrap_it_up(0);
  1602. X}
  1603. X
  1604. void
  1605. wrap_it_up(ret)
  1606. X{
  1607. X    unlink(lockfile);
  1608. X    exit(ret);
  1609. X}
  1610. X
  1611. void
  1612. active_times()
  1613. X{
  1614. X    FILE *fp_active, *fp_date_r, *fp_date_w;
  1615. X    register char *cp;
  1616. X
  1617. X    if ((fp_active = fopen(ACTIVE_FILE, "r")) == NULL) {
  1618. X    if (!daemon_delay) {
  1619. X        fprintf(stderr, "Unable to open active file.\n");
  1620. X    }
  1621. X    return;
  1622. X    }
  1623. X    if ((fp_date_r = fopen(ACTIVE_TIMES_FILE, "r")) == NULL) {
  1624. X    if (!daemon_delay) {
  1625. X        fprintf(stderr, "Creating active.times file.\n");
  1626. X    }
  1627. X    old_groups = 1;
  1628. X    } else {
  1629. X    old_groups = 0;
  1630. X    }
  1631. X    sprintf(buf, "%s.n", ACTIVE_TIMES_FILE);
  1632. X    if ((fp_date_w = fopen(buf, "w")) == NULL) {
  1633. X    if (!daemon_delay) {
  1634. X        fprintf(stderr, "Unable to create active.times file.\n");
  1635. X    }
  1636. X    return;
  1637. X    }
  1638. X
  1639. X    /* Loop through entire active file and remember each line. */
  1640. X    while (fgets(buf, sizeof buf, fp_active)) {
  1641. X    if (!(cp = index(buf, ' ')) || cp[1] == '\0') {
  1642. X        continue;
  1643. X    }
  1644. X    cp[1] = '\0';        /* include trailing space */
  1645. X    if (!(cp = rindex(cp + 2, ' '))) {
  1646. X        continue;
  1647. X    }
  1648. X    pline = (ACTIVE_LINE*)malloc(sizeof (ACTIVE_LINE));
  1649. X    if (!pline) {
  1650. X        if (line_root) {
  1651. X        last_line->link = NULL;
  1652. X        free_lines();
  1653. X        }
  1654. X      bug_out:
  1655. X        fclose(fp_active);
  1656. X        fclose(fp_date_r);
  1657. X        fclose(fp_date_w);
  1658. X        return;
  1659. X    }
  1660. X    pline->name = malloc(strlen(buf) + 1);
  1661. X    if (!pline->name) {
  1662. X        if (line_root) {
  1663. X        last_line->link = NULL;
  1664. X        pline->name = NULL;
  1665. X        free_lines();
  1666. X        } else {
  1667. X        free(pline);
  1668. X        }
  1669. X        goto bug_out;
  1670. X    }
  1671. X    strcpy(pline->name, buf);
  1672. X    pline->type = cp[1];
  1673. X    if (!last_line) {
  1674. X        line_root = pline;
  1675. X    } else {
  1676. X        last_line->link = pline;
  1677. X    }
  1678. X    last_line = pline;
  1679. X    }
  1680. X    last_line->link = NULL;
  1681. X    fclose(fp_active);
  1682. X
  1683. X    if (fp_date_r) {
  1684. X    /* Loop through date file, copying existing groups to new file. */
  1685. X    while (fgets(buf, sizeof buf, fp_date_r)) {
  1686. X        last_line = NULL;
  1687. X        for (pline = line_root; pline; pline = pline->link) {
  1688. X        if (strnEQ(buf, pline->name, strlen(pline->name))) {
  1689. X            fputs(buf, fp_date_w);
  1690. X            free(pline->name);
  1691. X            if (last_line) {
  1692. X            last_line->link = pline->link;
  1693. X            } else {
  1694. X            line_root = pline->link;
  1695. X            }
  1696. X            free(pline);
  1697. X            break;
  1698. X        }
  1699. X        last_line = pline;
  1700. X        }/* for */
  1701. X    }
  1702. X    }
  1703. X    /* Remaining entries from active file are new groups. */
  1704. X    for (pline = line_root; pline; pline = last_line) {
  1705. X    if (pline->type != 'x' && pline->type != '=') {
  1706. X        /* If it's not 'x'ed out, write it out with the current time. */
  1707. X        fprintf(fp_date_w, "%s%ld acttimes@%s\n", pline->name,
  1708. X        old_groups ? 30010440L : time(NULL), DOMAIN);
  1709. X    }
  1710. X    free(pline->name);
  1711. X    last_line = pline->link;
  1712. X    free(pline);
  1713. X    }
  1714. X    fclose(fp_date_w);
  1715. X    fclose(fp_date_r);
  1716. X    line_root = NULL;
  1717. X
  1718. X    /* rm active.times.o */
  1719. X    sprintf(buf,"%s.o", ACTIVE_TIMES_FILE);
  1720. X    unlink(buf);
  1721. X    /* mv active.times active.times.o */
  1722. X    link(ACTIVE_TIMES_FILE, buf);
  1723. X    unlink(ACTIVE_TIMES_FILE);
  1724. X    /* mv active.times.n active.times */
  1725. X    sprintf(buf, "%s.n", ACTIVE_TIMES_FILE);
  1726. X    link(buf, ACTIVE_TIMES_FILE);
  1727. X    unlink(buf);
  1728. X}
  1729. X
  1730. void
  1731. free_lines()
  1732. X{
  1733. X    for (pline = line_root; pline; pline = last_line) {
  1734. X    if (pline->name) {
  1735. X        free(pline->name);
  1736. X    }
  1737. X    last_line = pline->link;
  1738. X    free(pline);
  1739. X    }
  1740. X    line_root = NULL;
  1741. X}
  1742. END_OF_FILE
  1743. if test 10481 -ne `wc -c <'nntp/acttimes.c'`; then
  1744.     echo shar: \"'nntp/acttimes.c'\" unpacked with wrong size!
  1745. fi
  1746. # end of 'nntp/acttimes.c'
  1747. fi
  1748. if test -f 'nntp/xthread.patch' -a "${1}" != "-c" ; then 
  1749.   echo shar: Will not clobber existing file \"'nntp/xthread.patch'\"
  1750. else
  1751. echo shar: Extracting \"'nntp/xthread.patch'\" \(11249 characters\)
  1752. sed "s/^X//" >'nntp/xthread.patch' <<'END_OF_FILE'
  1753. Here's a modified version of Tim Iverson's xthread patch for nntp.  The code
  1754. that goes into the nntp daemon has been only slightly modified for use with
  1755. trn 2.0.  The clientlib code has been entirely re-worked, however.
  1756. X
  1757. If you plan to build a trn that uses the XTHREAD extention for NNTP,
  1758. you need to apply this patch from the root directory of your nntp source
  1759. before compiling trn.  Your server will need to be running a version of
  1760. nntpd that supports the XTHREAD call.  This can be either version 1.5.11t
  1761. created by this patch, or version 1.5.11a created by Tim's patch.
  1762. X
  1763. To apply this:
  1764. X    cd nntp-1.5.11
  1765. X    patch -p < xthread.patch
  1766. or    unipatch < xthread.patch | patch -p
  1767. X
  1768. This patch changes the conf.h.dist file, so you will need to edit your
  1769. conf.h file to add the new XTHREAD section.  For example, you could:
  1770. X
  1771. X    cd common
  1772. X    diff -c conf.h.dist.orig conf.h.dist | patch conf.h
  1773. X
  1774. and then edit conf.h to make sure THREAD_DIR is defined correctly.  If
  1775. you're not running C news, I also recommend that you set things up to use
  1776. the active.times file, and use "mthreads -t" or the program acttimes to
  1777. maintain it.  This allows trn to use the NEWGROUPS call in nntp, and have
  1778. it return accurate values.
  1779. X
  1780. Then you are ready to compile the new server with "make server" as usual.
  1781. X
  1782. Tim can be contacted at:  iverson@xstor.com -/- uunet!xstor!iverson
  1783. X-- 
  1784. X \  /| / /|\/ /| /(_)     Wayne Davison
  1785. X(_)/ |/ /\|/ / |/  \      davison@borland.com
  1786. X   (W   A  Y   N   e)
  1787. X
  1788. Index: common/version.c
  1789. Prereq: "1.5.11
  1790. X@@ -3,3 +3,3 @@
  1791. X  */
  1792. X-char    nntp_version[] = "1.5.11 (10 February 1991)";
  1793. X+char    nntp_version[] = "1.5.11t (16 November 1991)";
  1794. Index: common/clientlib.c
  1795. X@@ -606,4 +606,74 @@
  1796. X+static    long    rawbytes = -1;    /* bytes remaining to be transfered */
  1797. X+
  1798. X+/*
  1799. X+ * rawcheck_server -- get a line of text from the server, interpreting
  1800. X+ * it as a status message for a raw (binary) command.  Call this once
  1801. X+ * before calling rawget_server() for the actual data transfer.
  1802. X+ *
  1803. X+ *    Parameters:    "string" has the buffer space for the
  1804. X+ *            line received.
  1805. X+ *            "size" is the size of the buffer.
  1806. X+ *
  1807. X+ *    Returns:    -1 on error, otherwise the length of the raw data.
  1808. X+ *
  1809. X+ *    Side effects:    Talks to server, changes contents of "string".
  1810. X+ */
  1811. X+long
  1812. X+rawcheck_server(string, size)
  1813. X+char    *string;
  1814. X+int    size;
  1815. X+{
  1816. X+    /* try to get the status line and the status code */
  1817. X+    if (get_server(string, size) || *string != CHAR_OK)
  1818. X+        return rawbytes = -1;
  1819. X+
  1820. X+    /* try to get the number of bytes being transfered */
  1821. X+    if (sscanf(string, "%*d%ld", &rawbytes) != 1)
  1822. X+        return rawbytes = -1;
  1823. X+    return rawbytes;
  1824. X+}
  1825. X+
  1826. X+
  1827. X+/*
  1828. X+ * rawget_server -- read data from the server in raw format.  This call must
  1829. X+ * follow an appropriate put_server command and a rawcheck_server call.
  1830. X+ *
  1831. X+ *    Parameters:    "buf" is the buffer for the data to receive.
  1832. X+ *            "n" is the size of the buffer.
  1833. X+ *
  1834. X+ *    Returns:    0 on EOF, otherwise the length of the read.
  1835. X+ *
  1836. X+ *    Side effects:    Talks to server, changes contents of "buf".
  1837. X+ */
  1838. X+long
  1839. X+rawget_server(buf, n)
  1840. X+void    *buf;
  1841. X+long    n;
  1842. X+{
  1843. X+    /* if no bytes to read, then just return EOF */
  1844. X+    if (rawbytes < 0)
  1845. X+        return 0;
  1846. X+
  1847. X+    /* try to read some data from the server */
  1848. X+    if (rawbytes) {
  1849. X+        n = fread(buf, 1, n > rawbytes ? rawbytes : n, ser_rd_fp);
  1850. X+        rawbytes -= n;
  1851. X+    } else
  1852. X+        n = 0;
  1853. X+
  1854. X+    /* if no more left, then fetch the end-of-command signature */
  1855. X+    if (!rawbytes)
  1856. X+    {
  1857. X+        char buf[5];    /* "\r\n.\r\n" */
  1858. X+
  1859. X+        fread(buf, 1, 5, ser_rd_fp);
  1860. X+        rawbytes = -1;
  1861. X+    }
  1862. X+    return n;
  1863. X+}
  1864. X+
  1865. X+
  1866. X /*
  1867. X  * close_server -- close the connection to the server, after sending
  1868. X@@ -633,3 +703,2 @@
  1869. X     (void) fclose(ser_rd_fp);
  1870. X }
  1871. X-
  1872. Index: common/clientlib.h
  1873. X@@ -9,3 +9,5 @@
  1874. X extern    void    put_server();
  1875. X extern    int    get_server();
  1876. X+extern    long    rawcheck_server();
  1877. X+extern    long    rawget_server();
  1878. X extern    void    close_server();
  1879. Index: common/conf.h.dist
  1880. X@@ -95,4 +95,19 @@
  1881. X             /* loaded already, defining this may be a bad idea */
  1882. X+/* XTHREAD defines:  if XTHREAD is defined, THREAD_DIR controls where the
  1883. X+ * thread files will be read from.
  1884. X+ */
  1885. X+#define XTHREAD        /* Optional XTHREAD command.  This allows trn to
  1886. X+             * keep all data on the server. */
  1887. X+
  1888. X+/* Leave this undefined to indicate that thread files go in the spool
  1889. X+ * directory.  However, if you want a separate hierarchy of thread files,
  1890. X+ * define it here.
  1891. X+ */
  1892. X+/*#define THREAD_DIR    "/usr/spool/threads"        /* base directory */
  1893. X+
  1894. X+/* if LONG_THREAD_NAMES & THREAD_DIR are defined, create threads in one dir */
  1895. X+#undef LONG_THREAD_NAMES        /* not for short-name systems */
  1896. X+
  1897. X /* Things that vary in network implementations */
  1898. X #define    SUBNET        /* If you have 4.3 subnetting */
  1899. X@@ -333,4 +348,22 @@
  1900. X #        endif
  1901. X #    endif
  1902. X+#endif
  1903. X+
  1904. X+#ifdef XTHREAD
  1905. X+# ifdef THREAD_DIR
  1906. X+#   ifdef LONG_THREAD_NAMES
  1907. X+#    undef SUFFIX
  1908. X+#   else
  1909. X+#     ifndef SUFFIX
  1910. X+#    define SUFFIX ".th"
  1911. X+#     endif
  1912. X+#   endif
  1913. X+# else
  1914. X+#   define THREAD_DIR    SPOOLDIR
  1915. X+#   ifndef SUFFIX
  1916. X+#     define SUFFIX    "/.thread"
  1917. X+#   endif
  1918. X+#   undef LONG_THREAD_NAMES
  1919. X+# endif
  1920. X #endif
  1921. Index: common/nntp.h
  1922. X@@ -49,4 +49,5 @@
  1923. X #define    OK_AUTHSYS    280    /* Authorization system ok */
  1924. X #define    OK_AUTH        281    /* Authorization (user/pass) ok */
  1925. X+#define    OK_BIN        282    /* binary data follows */
  1926. X #define    CONT_XFER    335    /* Continue to send article */
  1927. Index: server/Makefile
  1928. X@@ -7,5 +7,5 @@
  1929. X     newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \
  1930. X     slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \
  1931. X-    batch.o auth.o timer.o ../common/version.o
  1932. X+    batch.o auth.o timer.o xthread.o ../common/version.o
  1933. X SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \
  1934. X@@ -13,5 +13,5 @@
  1935. X     newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \
  1936. X     slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \
  1937. X-    batch.c auth.c timer.c ../common/version.c
  1938. X+    batch.c auth.c timer.c xthread.c ../common/version.c
  1939. X SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h
  1940. Index: server/common.h
  1941. X@@ -164,4 +164,9 @@
  1942. X extern    char    inews[];
  1943. X extern    char    rnews[];
  1944. X+
  1945. X+#ifdef    XTHREAD
  1946. X+extern    char    *threaddir;
  1947. X+extern    char    *threadfile;
  1948. X+#endif
  1949. X extern    char    **group_array;
  1950. Index: server/globals.c
  1951. X@@ -27,4 +27,9 @@
  1952. X char    rnews[] = RNEWS;
  1953. X+#ifdef    XTHREAD
  1954. X+char    *threaddir = THREAD_DIR;
  1955. X+char    *threadfile = NULL;
  1956. X+#endif
  1957. X+
  1958. X /*
  1959. X  * Other random externals.
  1960. Index: server/group.c
  1961. X@@ -5,4 +5,8 @@
  1962. X #include "common.h"
  1963. X+#ifdef    XTHREAD
  1964. X+extern char *thread_name();
  1965. X+#endif
  1966. X+
  1967. X /*
  1968. X  * GROUP newsgroup
  1969. X@@ -97,4 +101,8 @@
  1970. X     ingroup = 1;
  1971. X+
  1972. X+#ifdef    XTHREAD
  1973. X+    threadfile = thread_name(argv[1]);
  1974. X+#endif
  1975. X     while ((cp = index(argv[1], '/')) != (char *) NULL)
  1976. Index: server/help.c
  1977. X@@ -22,6 +22,13 @@
  1978. X     printf("STAT        NEWGROUPS    HELP\r\n");
  1979. X     printf("IHAVE       NEWNEWS      SLAVE\r\n");
  1980. X-    printf("\r\nAdditionally, the following extention is supported:\r\n\r\n");
  1981. X+#if defined(XHDR) || defined(XTHREAD)
  1982. X+    printf("\r\nAdditionally, the following extentions are supported:\r\n\r\n");
  1983. X+# ifdef    XHDR
  1984. X     printf("XHDR        Retrieve a single header line from a range of articles.\r\n");
  1985. X+# endif    XHDR
  1986. X+# ifdef    XTHREAD
  1987. X+    printf("XTHREAD     Retrieve trn thread file for the current group.\r\n");
  1988. X+# endif
  1989. X+#endif
  1990. X     printf("\r\n");
  1991. X     printf("Bugs to Stan Barber (Internet: nntp@tmc.edu; UUCP: ...!bcm!nntp)\r\n");
  1992. Index: server/serve.c
  1993. X@@ -27,5 +27,5 @@
  1994. X extern    int    ahbs(), group(), help(), ihave();
  1995. X extern    int    list(), newgroups(), newnews(), nextlast(), post();
  1996. X-extern    int    slave(), stat(), xhdr();
  1997. X+extern    int    slave(), stat(), xhdr(), xthread();
  1998. X extern int errno;
  1999. X@@ -62,4 +62,7 @@
  2000. X     "xhdr",        0,    xhdr,
  2001. X #endif XHDR
  2002. X+#ifdef XTHREAD
  2003. X+    "xthread",    0,    xthread,
  2004. X+#endif
  2005. X };
  2006. X #define NUMCMDS (sizeof(cmdtbl) / sizeof(struct cmdent))
  2007. Index: server/xthread.c
  2008. X@@ -0,0 +1,152 @@
  2009. X+/* This file (and only this file - not the entire nntp distribution) is
  2010. X+ * hereby placed in the public domain.  Use it as you see fit, but if you
  2011. X+ * manage to find some wonderful use for this code elsewhere, it would be
  2012. X+ * nice to receive mention for it.
  2013. X+ *
  2014. X+ * - Tim Iverson
  2015. X+ *   iverson@xstor.com -/- uunet!xstor!iverson
  2016. X+ *   3/28/91
  2017. X+ *   modified by Wayne Davison (davison@borland.com) to work with trn 2.0
  2018. X+ *   10/6/91
  2019. X+ */
  2020. X+
  2021. X+#include "common.h"
  2022. X+
  2023. X+#ifdef XTHREAD
  2024. X+
  2025. X+# ifdef __GNUC__
  2026. X+#  define alloca __builtin_alloca
  2027. X+# endif
  2028. X+
  2029. X+
  2030. X+/* Usage: XTHREAD [DBINIT|THREAD]
  2031. X+ *
  2032. X+ * DBINIT    dump the contents of the db.init file to stdout
  2033. X+ * THREAD    dump the contents of the thread file for the current
  2034. X+ *        newsgroup to stdout (default if no argument).
  2035. X+ *
  2036. X+ * N.B. These two files are not ascii and no attempt is made at converting
  2037. X+ *    native byte size to any type of standard whatsoever.  This'll have
  2038. X+ *    to be fixed if this command is to be integrated into the protocol.
  2039. X+ *
  2040. X+ * This command is not documented in rfc977.
  2041. X+ */
  2042. X+
  2043. X+void
  2044. X+xthread(argc, argv)
  2045. X+int    argc;
  2046. X+char    *argv[];
  2047. X+{
  2048. X+    register FILE    *fp;
  2049. X+    struct stat    s;
  2050. X+    char        *buf, *file, *what;
  2051. X+
  2052. X+    /* can't transfer threads, only read 'em */
  2053. X+    if (!canread)
  2054. X+    {
  2055. X+        printf("%d You only have permission to transfer, sorry.\r\n",
  2056. X+            ERR_ACCESS);
  2057. X+        (void) fflush(stdout);
  2058. X+        return;
  2059. X+    }
  2060. X+
  2061. X+    /* "parse" the argument */
  2062. X+    if (argc == 2 && !strcasecmp(argv[1], "dbinit"))
  2063. X+    {
  2064. X+        char *cp, *thread_name();
  2065. X+
  2066. X+        file = thread_name("*******");
  2067. X+        what = "db.init";
  2068. X+        for (cp = file; *cp != '*'; cp++)
  2069. X+            ;
  2070. X+        strcpy(cp, what);
  2071. X+    }
  2072. X+    else if (argc == 1 || argc == 2 && !strcasecmp(argv[1], "thread"))
  2073. X+    {
  2074. X+        if (!threadfile)
  2075. X+        {
  2076. X+            printf("%d You are not currently in a newsgroup.\r\n",
  2077. X+                ERR_NCING);
  2078. X+            (void) fflush(stdout);
  2079. X+            return;
  2080. X+        }
  2081. X+        file = threadfile;
  2082. X+        what = "thread";
  2083. X+    }
  2084. X+    else
  2085. X+    {
  2086. X+        printf("%d Usage: XTHREAD [DBINIT|THREAD]\r\n", ERR_CMDSYN);
  2087. X+        (void) fflush(stdout);
  2088. X+        return;
  2089. X+    }
  2090. X+
  2091. X+    /* try to open the file to be transfered */
  2092. X+    if (!(fp = fopen(file, "r")))
  2093. X+    {
  2094. X+        printf("%d %s file is not available.\r\n", ERR_FAULT, what);
  2095. X+        (void) fflush(stdout);
  2096. X+#ifdef SYSLOG
  2097. X+        if (!strcmp(what, "db.init"))
  2098. X+            syslog(LOG_ERR, "xthread: fopen %s: %m", file);
  2099. X+#endif
  2100. X+        return;
  2101. X+    }
  2102. X+
  2103. X+    /* tell 'em how much binary data is coming down the pike */
  2104. X+    fstat(fileno(fp), &s);
  2105. X+    printf("%d %u bytes of %s file follows verbatim (binary!)\r\n",
  2106. X+        OK_BIN, s.st_size, what);
  2107. X+
  2108. X+    /* copy the file verbatim to stdout */
  2109. X+#ifdef __GNUC__
  2110. X+    if (buf = alloca(s.st_size))
  2111. X+    {
  2112. X+        /* ah-so! got lotsa memoree */
  2113. X+        read(fileno(fp), buf, s.st_size);
  2114. X+        fwrite(buf, s.st_size, 1, stdout);
  2115. X+    }
  2116. X+    else
  2117. X+#endif
  2118. X+    {
  2119. X+        int        bytes;
  2120. X+        char        buf[BUFSIZ];
  2121. X+
  2122. X+        while (bytes = fread(buf, 1, sizeof buf, fp))
  2123. X+            fwrite(buf, bytes, 1, stdout);
  2124. X+    }
  2125. X+
  2126. X+    fputs("\r\n.\r\n", stdout);
  2127. X+    fflush(stdout);
  2128. X+    fclose(fp);
  2129. X+}
  2130. X+
  2131. X+/* Change a newsgroup name into the name of the thread data file.  We
  2132. X+** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES
  2133. X+** is defined), prepend the path, and append the '/.thread' (or '.th') on to
  2134. X+** the end.
  2135. X+*/
  2136. X+char *
  2137. X+thread_name(group)
  2138. X+char *group;
  2139. X+{
  2140. X+    static char name_buff[MAXPATHLEN];
  2141. X+#ifndef LONG_THREAD_NAMES
  2142. X+    char group_buff[512];
  2143. X+    register char *ptr;
  2144. X+
  2145. X+    strcpy(group_buff, group);
  2146. X+    ptr = group = group_buff;
  2147. X+    while ((ptr = index(ptr, '.'))) {
  2148. X+        *ptr = '/';
  2149. X+    }
  2150. X+#endif
  2151. X+#ifdef SUFFIX
  2152. X+    sprintf(name_buff, "%s/%s%s", threaddir, group, SUFFIX);
  2153. X+#else
  2154. X+    sprintf(name_buff, "%s/%s", threaddir, group);
  2155. X+#endif
  2156. X+
  2157. X+    return name_buff;
  2158. X+}
  2159. X+
  2160. X+#endif /* not XTHREAD */
  2161. END_OF_FILE
  2162. if test 11249 -ne `wc -c <'nntp/xthread.patch'`; then
  2163.     echo shar: \"'nntp/xthread.patch'\" unpacked with wrong size!
  2164. fi
  2165. # end of 'nntp/xthread.patch'
  2166. fi
  2167. if test -f 'rthreads.c' -a "${1}" != "-c" ; then 
  2168.   echo shar: Will not clobber existing file \"'rthreads.c'\"
  2169. else
  2170. echo shar: Extracting \"'rthreads.c'\" \(10171 characters\)
  2171. sed "s/^X//" >'rthreads.c' <<'END_OF_FILE'
  2172. X/* $Id: rthreads.c,v 4.4.3.1 1991/11/22 04:12:18 davison Trn $
  2173. X**
  2174. X** $Log: rthreads.c,v $
  2175. X** Revision 4.4.3.1  1991/11/22  04:12:18  davison
  2176. X** Trn Release 2.0
  2177. X** 
  2178. X*/
  2179. X
  2180. X#include "EXTERN.h"
  2181. X#include "common.h"
  2182. X#include "intrp.h"
  2183. X#include "rn.h"
  2184. X#include "ngdata.h"
  2185. X#include "rcln.h"
  2186. X#include "bits.h"
  2187. X#include "util.h"
  2188. X#ifndef USETMPTHREAD
  2189. X#include "rcstuff.h"
  2190. X#endif
  2191. X#ifdef SERVER
  2192. X#include "server.h"
  2193. X#endif
  2194. X
  2195. X#ifdef USETHREADS
  2196. X#include "threads.h"
  2197. X#include "INTERN.h"
  2198. X#include "rthreads.h"
  2199. X
  2200. static FILE *fp;
  2201. X
  2202. static char *strings = Nullch;
  2203. X
  2204. static bool tmpthread_created = FALSE;
  2205. X
  2206. static int read_item();
  2207. static void safefree();
  2208. X
  2209. X/* Initialize our thread code by determining the byte-order of the thread
  2210. X** files and our own current byte-order.  If they differ, set flags to let
  2211. X** the read code know what we'll need to translate.
  2212. X*/
  2213. void
  2214. thread_init()
  2215. X{
  2216. X    int i;
  2217. X#ifdef XTHREAD
  2218. X    long size;
  2219. X#endif
  2220. X
  2221. X    tmpthread_file = savestr(filexp("/tmp/thread.%$"));
  2222. X
  2223. X    word_same = long_same = TRUE;
  2224. X#ifdef XTHREAD
  2225. X    sprintf(ser_line, "XTHREAD DBINIT");
  2226. X# ifdef DEBUGGING
  2227. X    if (debug & DEB_NNTP) {
  2228. X    printf(">%s\n", ser_line) FLUSH;
  2229. X    }
  2230. X# endif
  2231. X    put_server(ser_line);
  2232. X    rawcheck_server(ser_line, sizeof ser_line);
  2233. X# ifdef DEBUGGING
  2234. X    if (debug & DEB_NNTP) {
  2235. X    printf("<%s\n", ser_line) FLUSH;
  2236. X    }
  2237. X# endif
  2238. X    size = rawget_server((char*)&mt_bmap, sizeof (BMAP));
  2239. X    if (size >= sizeof (BMAP) - 1) {
  2240. X#else /* !XTHREAD */
  2241. X    if ((fp = fopen(filexp(DBINIT), FOPEN_RB)) != Nullfp
  2242. X     && fread((char*)&mt_bmap, 1, sizeof(BMAP), fp) >= sizeof (BMAP) - 1) {
  2243. X#endif
  2244. X    if (mt_bmap.version != DB_VERSION) {
  2245. X        printf("\nThread database is the wrong version -- ignoring it.\n")
  2246. X        FLUSH;
  2247. X        use_threads = FALSE;
  2248. X    }
  2249. X    mybytemap(&my_bmap);
  2250. X    for (i = 0; i < sizeof (LONG); i++) {
  2251. X        if (i < sizeof (WORD)) {
  2252. X        if (my_bmap.w[i] != mt_bmap.w[i]) {
  2253. X            word_same = FALSE;
  2254. X        }
  2255. X        }
  2256. X        if (my_bmap.l[i] != mt_bmap.l[i]) {
  2257. X        long_same = FALSE;
  2258. X        }
  2259. X    }
  2260. X    } else {
  2261. X    printf("\ndb.init read failed -- assuming no byte-order translations.\n\n") FLUSH;
  2262. X    }
  2263. X#ifdef XTHREAD
  2264. X    while (rawget_server(ser_line, sizeof ser_line)) {
  2265. X    ;        /* trash any extraneous bytes */
  2266. X    }
  2267. X#else
  2268. X    if (fp != Nullfp) {
  2269. X    fclose(fp);
  2270. X    }
  2271. X#endif
  2272. X}
  2273. X
  2274. X/* Open a thread file to make use of the data it contains.  Everything is
  2275. X** slurped into arrays and buffers, and some minor digesting of the data
  2276. X** is performed to make it more palatable.  Be sure to call unuse_data()
  2277. X** before calling this again.
  2278. X*/
  2279. int
  2280. use_data(thread_if_empty)
  2281. bool_int thread_if_empty;
  2282. X{
  2283. X    register int i, j, k;
  2284. X    register char *ptr;
  2285. X    char *threadname;
  2286. X    bool already_tried = FALSE;
  2287. X    ART_NUM last, first;
  2288. X
  2289. X    last = getngsize(ng);
  2290. X    first = getabsfirst(ng, last);
  2291. X    if (last < first) {
  2292. X    return 0;
  2293. X    }
  2294. X    if (tmpthread_group && strEQ(ngname, tmpthread_group)) {
  2295. X    threadname = tmpthread_file;
  2296. X    } else {
  2297. X#ifdef XTHREAD        /* use remote thread file? */
  2298. X    long size;
  2299. X
  2300. X    threadname = Nullch;        /* assume we fail */
  2301. X    sprintf(ser_line, "XTHREAD THREAD");
  2302. X# ifdef DEBUGGING
  2303. X    if (debug & DEB_NNTP) {
  2304. X        printf(">%s\n", ser_line) FLUSH;
  2305. X    }
  2306. X# endif
  2307. X    put_server(ser_line);
  2308. X    size = rawcheck_server(ser_line, sizeof ser_line);
  2309. X# ifdef DEBUGGING
  2310. X    if (debug & DEB_NNTP) {
  2311. X        printf("<%s\n", ser_line) FLUSH;
  2312. X    }
  2313. X# endif
  2314. X    if (size < 0) {
  2315. X        fp = Nullfp;
  2316. X    } else {
  2317. X        char *tmpbuf;
  2318. X        long bufsize;
  2319. X
  2320. X# ifdef SPEEDOVERMEM
  2321. X        bufsize = size ? size : 1;
  2322. X# else
  2323. X        bufsize = 16384;
  2324. X# endif
  2325. X        fp = fopen(tmpthread_file, "w");
  2326. X        tmpbuf = safemalloc(bufsize);
  2327. X        while ((size = rawget_server(tmpbuf, bufsize)) != 0) {
  2328. X        if (fp != Nullfp) {
  2329. X            /* write it out unbuffered */
  2330. X            write(fileno(fp), tmpbuf, size);
  2331. X        }
  2332. X        }
  2333. X        free(tmpbuf);
  2334. X        if (fp != Nullfp) {
  2335. X        fclose(fp);
  2336. X        growstr(&tmpthread_group, &tmpthread_glen,
  2337. X            strlen(ngname) + 1);
  2338. X        strcpy(tmpthread_group, ngname);
  2339. X        tmpthread_created = FALSE;
  2340. X        threadname = tmpthread_file;
  2341. X        }
  2342. X    }
  2343. X#else /* !XTHREAD */
  2344. X    threadname = thread_name(ngname);
  2345. X#endif
  2346. X    }
  2347. try_to_use:
  2348. X#ifdef XTHREAD
  2349. X    if (!threadname || (fp = fopen(threadname, FOPEN_RB)) == Nullfp) {
  2350. X    if (threadname && errno != ENOENT) {
  2351. X#else
  2352. X    if ((fp = fopen(threadname, FOPEN_RB)) == Nullfp) {
  2353. X    if (errno != ENOENT) {
  2354. X#endif
  2355. X        printf("\n\nOpen failed for thread data.\n");
  2356. X    }
  2357. X    bzero(&total, sizeof (TOTAL));
  2358. X    if (!thread_if_empty || already_tried) {
  2359. X        return 0;
  2360. X    }
  2361. X    } else if (fread((char*)&total, 1, sizeof(TOTAL), fp) < sizeof(TOTAL)) {
  2362. X    fclose(fp);
  2363. X    bzero(&total, sizeof (TOTAL));
  2364. X    if (already_tried) {
  2365. X        return 0;
  2366. X    }
  2367. X    } else if (threadname != tmpthread_file || !tmpthread_created) {
  2368. X    lp_bmap(&total.first, 4);
  2369. X    wp_bmap(&total.root, 5);
  2370. X    } else if (!total.root) {
  2371. X    fclose(fp);
  2372. X    return 0;
  2373. X    }
  2374. X
  2375. X    if (total.last < last) {
  2376. X#ifdef USETMPTHREAD
  2377. X    char cmd[512];
  2378. X    ART_NUM max = last - first + 1;
  2379. X
  2380. X    if (fp) {
  2381. X        fclose(fp);
  2382. X    }
  2383. X    sprintf(cmd, "tmpthread %s %s %ld %ld %ld %s",
  2384. X#ifdef XTHREAD
  2385. X        threadname == tmpthread_file ?
  2386. X            (tmpthread_created ? "-t" : "-T") : nullstr,
  2387. X#else
  2388. X        threadname == tmpthread_file ? "-t" : nullstr,
  2389. X#endif
  2390. X        ngname, (long)last, (long)first, (long)max, tmpthread_file);
  2391. X    if (system(filexp(cmd))) {
  2392. X        printf("\n\nFailed to thread data -- continuing unthreaded.\n");
  2393. X        if (tmpthread_group) {
  2394. X        *tmpthread_group = '\0';
  2395. X        }
  2396. X        return 0;
  2397. X    }
  2398. X    growstr(&tmpthread_group, &tmpthread_glen, strlen(ngname) + 1);
  2399. X    strcpy(tmpthread_group, ngname);
  2400. X    threadname = tmpthread_file;
  2401. X    tmpthread_created = TRUE;
  2402. X    already_tried = TRUE;
  2403. X    goto try_to_use;
  2404. X#else /* !USETMPTHREAD */
  2405. X    if (lastart > total.last) {
  2406. X        tobethreaded = last - total.last;
  2407. X        toread[ng] -= tobethreaded;
  2408. X        lastart = total.last;
  2409. X    }
  2410. X#endif /* !USETMPTHREAD */
  2411. X    }
  2412. X
  2413. X    if (total.last > lastart) {
  2414. X#ifdef SERVER
  2415. X    fclose(actfp);
  2416. X    ngdata_init();        /* re-grab the active file */
  2417. X#endif
  2418. X    grow_ctl(total.last);    /* sets lastart */
  2419. X    ngmax[ng] = lastart;    /* ensure getngsize() knows the new maximum */
  2420. X    }
  2421. X
  2422. X    if (!read_item(&author_cnts, (MEM_SIZE)total.author * sizeof (WORD))
  2423. X     || !read_item(&strings, (MEM_SIZE)total.string1) 
  2424. X     || !read_item(&subject_cnts, (MEM_SIZE)total.subject * sizeof (WORD))
  2425. X     || !read_item(&p_roots, (MEM_SIZE)total.root * sizeof (PACKED_ROOT))
  2426. X     || !read_item(&p_articles, (MEM_SIZE)total.article * sizeof (PACKED_ARTICLE))) {
  2427. X    printf("\n\nRead failed for thread data -- continuing unthreaded.\n");
  2428. X    fclose(fp);
  2429. X    unuse_data(0);
  2430. X    return 0;
  2431. X    }
  2432. X    fclose(fp);
  2433. X
  2434. X    if ((threadname != tmpthread_file || !tmpthread_created)
  2435. X     && (!word_same || !long_same)) {
  2436. X    wp_bmap(author_cnts, total.author);
  2437. X    wp_bmap(subject_cnts, total.subject);
  2438. X    for (i = 0; i < total.root; i++) {
  2439. X        lp_bmap(&p_roots[i].root_num, 1);
  2440. X        wp_bmap(&p_roots[i].articles, 3);
  2441. X    }
  2442. X    for (i = 0; i < total.article; i++) {
  2443. X        lp_bmap(&p_articles[i].num, 2);
  2444. X        wp_bmap(&p_articles[i].subject, 8);
  2445. X    }
  2446. X    }
  2447. X
  2448. X#ifndef lint
  2449. X    author_ptrs = (char **)safemalloc(total.author * sizeof (char **));
  2450. X    subject_ptrs = (char **)safemalloc(total.subject * sizeof (char **));
  2451. X    root_subjects = (WORD *)safemalloc(total.root * sizeof (WORD));
  2452. X    root_article_cnts = (WORD *)safemalloc(total.root * sizeof (WORD));
  2453. X#endif
  2454. X    selected_roots = safemalloc(total.root * sizeof (char));
  2455. X
  2456. X    bzero(root_article_cnts, total.root * sizeof (WORD));
  2457. X    bzero(selected_roots, total.root * sizeof (char));
  2458. X
  2459. X    for (i = 0, ptr = strings; i < total.author; i++) {
  2460. X    author_ptrs[i] = ptr;
  2461. X    ptr += strlen(ptr) + 1;
  2462. X    }
  2463. X
  2464. X    for (i = 0, j = 0; i < total.root; i++) {
  2465. X    root_subjects[i] = j;
  2466. X    k = p_roots[i].subject_cnt;
  2467. X    while (k--) {
  2468. X        root_article_cnts[i] += subject_cnts[j];
  2469. X        subject_ptrs[j++] = ptr;
  2470. X        ptr += strlen(ptr) + 1;
  2471. X    }
  2472. X    if (saved_selections) {
  2473. X        for (k = 0; k < selected_root_cnt; k++) {
  2474. X        if (p_roots[i].root_num == saved_selections[k]) {
  2475. X            selected_roots[i] = 1;
  2476. X            break;
  2477. X        }
  2478. X        }
  2479. X    }
  2480. X    }
  2481. X    count_roots(!saved_selections);
  2482. X
  2483. X    /* Try to clean up the bitmap if articles are missing. */
  2484. X    if (unthreaded) {
  2485. X      char *newarea, *oldarea = ctlarea;
  2486. X      extern MEM_SIZE ctlsize;
  2487. X
  2488. X    newarea = ctlarea = safemalloc(ctlsize);
  2489. X    bzero(ctlarea, ctlsize);
  2490. X    for (i = total.article, p_art = p_articles; i--; p_art++) {
  2491. X        if (p_art->num >= firstbit) {
  2492. X        ctl_set(p_art->num);
  2493. X        }
  2494. X    }
  2495. X    for (i = firstbit; i <= lastart; i++) {
  2496. X        if (!ctl_read(i)) {
  2497. X        ctlarea = oldarea;
  2498. X        oneless(i);
  2499. X        ctlarea = newarea;
  2500. X        }
  2501. X    }
  2502. X    ctlarea = oldarea;
  2503. X    free(newarea);
  2504. X    p_art = Nullart;
  2505. X    count_roots(FALSE);
  2506. X    }
  2507. X    safefree(&saved_selections);
  2508. X    select_page = 0;
  2509. X    return 1;
  2510. X}
  2511. X
  2512. X/* A shorthand for reading a chunk of the file into a malloced array.
  2513. X*/
  2514. static int
  2515. read_item(dest, len)
  2516. char **dest;
  2517. MEM_SIZE len;
  2518. X{
  2519. X    int ret;
  2520. X
  2521. X    *dest = safemalloc(len);
  2522. X    ret = fread(*dest, 1, (int)len, fp);
  2523. X    if (ret != len) {
  2524. X    free(*dest);
  2525. X    *dest = Nullch;
  2526. X    return 0;
  2527. X    }
  2528. X    return 1;
  2529. X}
  2530. X
  2531. X/* Free some memory if it hasn't already been freed.
  2532. X*/
  2533. static void
  2534. safefree(pp)
  2535. char **pp;
  2536. X{
  2537. X    if (*pp) {
  2538. X    free(*pp);
  2539. X    *pp = Nullch;
  2540. X    }
  2541. X}
  2542. X
  2543. X/* Discard the thread data that we received through the use_data() call.
  2544. X** If "save_selections" is non-zero, we'll try to remember which roots
  2545. X** are currently selected long enough for the use_data() call to re-use
  2546. X** them.  Only do this when you are going to re-open the same data file
  2547. X** immediately with use_data() (presumably because the data has been
  2548. X** updated while we were using it).
  2549. X*/
  2550. void
  2551. unuse_data(save_selections)
  2552. bool_int save_selections;
  2553. X{
  2554. X    int i, j;
  2555. X
  2556. X    if (save_selections) {
  2557. X#ifndef lint
  2558. X    saved_selections
  2559. X      = (ART_NUM *)safemalloc(selected_root_cnt * sizeof (ART_NUM));
  2560. X#endif
  2561. X    for (i = 0, j = 0; i < total.root; i++) {
  2562. X        if (selected_roots[i]) {
  2563. X        saved_selections[j++] = p_roots[i].root_num;
  2564. X        }
  2565. X    }
  2566. X    } else {
  2567. X    selected_root_cnt = selected_count = 0;
  2568. X    }
  2569. X    safefree(&p_roots);
  2570. X    safefree(&root_subjects);
  2571. X    safefree(&author_cnts);
  2572. X    safefree(&subject_cnts);
  2573. X    safefree(&author_ptrs);
  2574. X    safefree(&subject_ptrs);
  2575. X    safefree(&root_article_cnts);
  2576. X    safefree(&selected_roots);
  2577. X    safefree(&p_articles);
  2578. X    safefree(&strings);
  2579. X
  2580. X    p_art = curr_p_art = Nullart;
  2581. X    init_tree();        /* free any tree lines */
  2582. X
  2583. X    bzero(&total, sizeof (TOTAL));
  2584. X}
  2585. X
  2586. X#endif /* USETHREADS */
  2587. END_OF_FILE
  2588. if test 10171 -ne `wc -c <'rthreads.c'`; then
  2589.     echo shar: \"'rthreads.c'\" unpacked with wrong size!
  2590. fi
  2591. # end of 'rthreads.c'
  2592. fi
  2593. if test -f 'term.h' -a "${1}" != "-c" ; then 
  2594.   echo shar: Will not clobber existing file \"'term.h'\"
  2595. else
  2596. echo shar: Extracting \"'term.h'\" \(9410 characters\)
  2597. sed "s/^X//" >'term.h' <<'END_OF_FILE'
  2598. X/* $Id: term.h,v 4.4 1991/09/09 20:27:37 sob Exp sob $
  2599. X *
  2600. X * $Log: term.h,v $
  2601. X * Revision 4.4  1991/09/09  20:27:37  sob
  2602. X * release 4.4
  2603. X *
  2604. X *
  2605. X * 
  2606. X */
  2607. X/* This software is Copyright 1991 by Stan Barber. 
  2608. X *
  2609. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  2610. X * use this software as long as: there is no monetary profit gained
  2611. X * specifically from the use or reproduction of this software, it is not
  2612. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  2613. X * included prominently in any copy made. 
  2614. X *
  2615. X * The author make no claims as to the fitness or correctness of this software
  2616. X * for any use whatsoever, and it is provided as is. Any use of this software
  2617. X * is at the user's own risk. 
  2618. X */
  2619. X
  2620. X#ifdef PUSHBACK
  2621. XEXT char circlebuf[PUSHSIZE];
  2622. XEXT int nextin INIT(0);
  2623. XEXT int nextout INIT(0);
  2624. X#ifdef PENDING
  2625. X#ifdef FIONREAD
  2626. XEXT long iocount INIT(0);
  2627. X#ifndef lint
  2628. X#define input_pending() (nextin!=nextout || (ioctl(0, FIONREAD, &iocount),(int)iocount))
  2629. X#else
  2630. X#define input_pending() bizarre
  2631. X#endif /* lint */
  2632. X#else /* FIONREAD */
  2633. X#ifdef RDCHK
  2634. X#define input_pending() (rdchk(0) > 0)        /* boolean only */
  2635. X#else /*  RDCHK */
  2636. int circfill();
  2637. XEXT int devtty INIT(0);
  2638. X#ifndef lint
  2639. X#define input_pending() (nextin!=nextout || circfill())
  2640. X#else
  2641. X#define input_pending() bizarre
  2642. X#endif /* lint */
  2643. X#endif /* RDCHK */
  2644. X#endif /* FIONREAD */
  2645. X#else /* PENDING */
  2646. X#ifndef lint
  2647. X#define input_pending() (nextin!=nextout)
  2648. X#else
  2649. X#define input_pending() bizarre
  2650. X#endif /* lint */
  2651. X#endif /* PENDING */
  2652. X#else /* PUSHBACK */
  2653. X#ifdef PENDING
  2654. X#ifdef FIONREAD    /* must have FIONREAD or O_NDELAY for input_pending() */
  2655. X#define read_tty(addr,size) read(0,addr,size)
  2656. X#ifndef lint
  2657. X#define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount)
  2658. X#else
  2659. X#define input_pending() bizarre
  2660. X#endif /* lint */
  2661. XEXT long iocount INIT(0);
  2662. X
  2663. X#else /* FIONREAD */
  2664. X
  2665. X#ifdef RDCHK
  2666. X#define input_pending() (rdchk(0) > 0)        /* boolean only */
  2667. X#else /*  RDCHK */
  2668. X
  2669. XEXT int devtty INIT(0);
  2670. XEXT bool is_input INIT(FALSE);
  2671. XEXT char pending_ch INIT(0);
  2672. X#ifndef lint
  2673. X#define input_pending() (is_input || (is_input=read(devtty,&pending_ch,1)))
  2674. X#else
  2675. X#define input_pending() bizarre
  2676. X#endif /* lint */
  2677. X#endif /*  RDCHK */
  2678. X#endif /* FIONREAD */
  2679. X#else /* PENDING */
  2680. X#define read_tty(addr,size) read(0,addr,size)
  2681. X#define input_pending() (FALSE)
  2682. X#endif /* PENDING */
  2683. X#endif /* PUSHBACK */
  2684. X
  2685. X/* stuff wanted by terminal mode diddling routines */
  2686. X
  2687. X#ifdef TERMIO
  2688. XEXT struct termio _tty, _oldtty;
  2689. X#else
  2690. X# ifdef TERMIOS
  2691. XEXT struct termios _tty, _oldtty;
  2692. X# else
  2693. XEXT struct sgttyb _tty;
  2694. XEXT int _res_flg INIT(0);
  2695. X# endif
  2696. X#endif
  2697. X
  2698. XEXT int _tty_ch INIT(2);
  2699. XEXT bool bizarre INIT(FALSE);            /* do we need to restore terminal? */
  2700. X
  2701. X/* terminal mode diddling routines */
  2702. X
  2703. X#ifdef TERMIO
  2704. X
  2705. X#define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty))
  2706. X#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty))
  2707. X#define echo()     ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty))
  2708. X#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty))
  2709. X#define nl()     ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
  2710. X#define nonl()     ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty))
  2711. X#define    savetty() (ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty))
  2712. X#define    resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty))
  2713. X#define unflush_output()
  2714. X
  2715. X#else /* !TERMIO */
  2716. X# ifdef TERMIOS
  2717. X
  2718. X#define crmode() ((bizarre=1), _tty.c_lflag &= ~ICANON,_tty.c_cc[VMIN]=1,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  2719. X#define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,tcsetattr(_tty_ch, TCSAFLUSH,&_tty))
  2720. X#define echo()     ((bizarre=1),_tty.c_lflag |= ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  2721. X#define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  2722. X#define nl()     ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  2723. X#define nonl()     ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,tcsetattr(_tty_ch, TCSAFLUSH, &_tty))
  2724. X#define    savetty() (tcgetattr(_tty_ch, &_oldtty),tcgetattr(_tty_ch, &_tty))
  2725. X#define    resetty() ((bizarre=0),tcsetattr(_tty_ch, TCSAFLUSH, &_oldtty))
  2726. X#define unflush_output()
  2727. X
  2728. X# else /* !TERMIOS */
  2729. X
  2730. X#define raw()     ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty))
  2731. X#define noraw()     ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty))
  2732. X#define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty))
  2733. X#define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty))
  2734. X#define echo()     ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty))
  2735. X#define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty))
  2736. X#define nl()     ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty))
  2737. X#define nonl()     ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty))
  2738. X#define    savetty() (gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags)
  2739. X#define    resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty))
  2740. X#  ifdef LFLUSHO
  2741. X#   ifndef lint
  2742. XEXT int lflusho INIT(LFLUSHO);
  2743. X#   else
  2744. XEXT long lflusho INIT(LFLUSHO);
  2745. X#   endif /* lint */
  2746. X#define unflush_output() (ioctl(_tty_ch,TIOCLBIC,&lflusho))
  2747. X#  else
  2748. X#define unflush_output()
  2749. X#  endif /* LFLUSHO */
  2750. X# endif /* TERMIOS */
  2751. X
  2752. X#endif /* TERMIO */
  2753. X
  2754. X#ifdef TIOCSTI
  2755. X#ifdef lint
  2756. X#define forceme(c) ioctl(_tty_ch,TIOCSTI,Null(long*))    /* ghad! */
  2757. X#else
  2758. X#define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */
  2759. X#endif /* lint */
  2760. X#else
  2761. X#define forceme(c)
  2762. X#endif
  2763. X
  2764. X/* termcap stuff */
  2765. X
  2766. X/*
  2767. X * NOTE: if you don't have termlib you'll either have to define these strings
  2768. X *    and the tputs routine, or you'll have to redefine the macros below
  2769. X */
  2770. X
  2771. X#ifdef HAVETERMLIB
  2772. XEXT int GT;                /* hardware tabs */
  2773. XEXT char *BC INIT(Nullch);        /* backspace character */
  2774. XEXT char *UP INIT(Nullch);        /* move cursor up one line */
  2775. XEXT char *CR INIT(Nullch);        /* get to left margin, somehow */
  2776. XEXT char *VB INIT(Nullch);        /* visible bell */
  2777. XEXT char *CL INIT(Nullch);        /* home and clear screen */
  2778. XEXT char *CE INIT(Nullch);        /* clear to end of line */
  2779. XEXT char *TI INIT(Nullch);        /* initialize terminal */
  2780. XEXT char *TE INIT(Nullch);        /* reset terminal */
  2781. X#if defined(CLEAREOL) || defined(USETHREADS)
  2782. XEXT char *CM INIT(Nullch);        /* cursor motion */
  2783. XEXT char *HO INIT(Nullch);        /* home cursor */
  2784. X#endif
  2785. X#ifdef CLEAREOL
  2786. XEXT char *CD INIT(Nullch);        /* clear to end of display */
  2787. X#endif /* CLEAREOL */
  2788. XEXT char *SO INIT(Nullch);        /* begin standout mode */
  2789. XEXT char *SE INIT(Nullch);        /* end standout mode */
  2790. XEXT int SG INIT(0);            /* blanks left by SO and SE */
  2791. XEXT char *US INIT(Nullch);        /* start underline mode */
  2792. XEXT char *UE INIT(Nullch);        /* end underline mode */
  2793. XEXT char *UC INIT(Nullch);        /* underline a character,
  2794. X                         if that's how it's done */
  2795. XEXT int UG INIT(0);            /* blanks left by US and UE */
  2796. XEXT bool AM INIT(FALSE);        /* does terminal have automatic
  2797. X                                 margins? */
  2798. XEXT bool XN INIT(FALSE);        /* does it eat 1st newline after
  2799. X                             automatic wrap? */
  2800. XEXT char PC INIT(0);            /* pad character for use by tputs() */
  2801. X
  2802. X#ifdef _POSIX_SOURCE
  2803. XEXT speed_t outspeed INIT(0);        /* terminal output speed, */
  2804. X#else
  2805. XEXT long outspeed INIT(0);        /*     for use by tputs() */
  2806. X#endif
  2807. X
  2808. XEXT int LINES INIT(0), COLS INIT(0);    /* size of screen */
  2809. XEXT int just_a_sec INIT(960);        /* 1 sec at current baud rate */
  2810. X                    /* (number of nulls) */
  2811. X
  2812. X/* define a few handy macros */
  2813. X
  2814. X#define backspace() tputs(BC,0,putchr) FLUSH
  2815. X#define clear() tputs(CL,LINES,putchr) FLUSH
  2816. X#define erase_eol() tputs(CE,1,putchr) FLUSH
  2817. X#ifdef CLEAREOL
  2818. X#define clear_rest() tputs(CD,LINES,putchr) FLUSH
  2819. X#define maybe_eol() if(erase_screen&&can_home_clear)tputs(CE,1,putchr) FLUSH
  2820. X#endif /* CLEAREOL */
  2821. X#define underline() tputs(US,1,putchr) FLUSH
  2822. X#define un_underline() tputs(UE,1,putchr) FLUSH
  2823. X#define underchar() tputs(UC,0,putchr) FLUSH
  2824. X#define standout() tputs(SO,1,putchr) FLUSH
  2825. X#define un_standout() tputs(SE,1,putchr) FLUSH
  2826. X#define up_line() tputs(UP,1,putchr) FLUSH
  2827. X#define carriage_return() tputs(CR,1,putchr) FLUSH
  2828. X#define dingaling() tputs(VB,1,putchr) FLUSH
  2829. X#else
  2830. X  ????????        /* up to you */
  2831. X#endif
  2832. X
  2833. XEXT int page_line INIT(1);    /* line number for paging in
  2834. X                         print_line (origin 1) */
  2835. X
  2836. void    term_init ANSI((void));
  2837. void    term_set ANSI((char *));
  2838. X#ifdef PUSHBACK
  2839. void    pushchar ANSI((char_int));
  2840. void    mac_init ANSI((char *));
  2841. void    mac_line ANSI((char *,char *,int));
  2842. void    show_macros ANSI((void));
  2843. X#endif
  2844. char    putchr ANSI((char_int));    /* routine for tputs to call */
  2845. bool    finish_command ANSI((int));
  2846. void    eat_typeahead ANSI((void));
  2847. void    settle_down ANSI((void));
  2848. X#ifdef HAVETERMLIB
  2849. void    termlib_init ANSI((void));
  2850. void    termlib_reset ANSI((void));
  2851. X#endif
  2852. X#ifndef read_tty
  2853. X    int        read_tty ANSI((char *,int));
  2854. X#endif
  2855. void    underprint ANSI((char *));
  2856. X#ifdef NOFIREWORKS
  2857. X    void    no_sofire ANSI((void));
  2858. X    void    no_ulfire ANSI((void));
  2859. X#endif
  2860. void    getcmd ANSI((char *));
  2861. int    get_anything ANSI((void));
  2862. void    in_char ANSI((char *,char_int));
  2863. int    print_lines ANSI((char *,int));
  2864. void    page_init ANSI((void));
  2865. void    pad ANSI((int));
  2866. void    printcmd ANSI((void));
  2867. void    rubout ANSI((void));
  2868. void    reprint ANSI((void));
  2869. X#if defined(CLEAREOL) || defined(USETHREADS)
  2870. void    home_cursor ANSI((void));
  2871. X#endif
  2872. X#ifdef USETHREADS
  2873. void    goto_line ANSI((int,int));
  2874. X#endif
  2875. X#ifdef SIGWINCH
  2876. SIGRET    winch_catcher ANSI((void));
  2877. X#endif /* SIGWINCH */
  2878. END_OF_FILE
  2879. if test 9410 -ne `wc -c <'term.h'`; then
  2880.     echo shar: \"'term.h'\" unpacked with wrong size!
  2881. fi
  2882. # end of 'term.h'
  2883. fi
  2884. if test -f 'uudecode.c' -a "${1}" != "-c" ; then 
  2885.   echo shar: Will not clobber existing file \"'uudecode.c'\"
  2886. else
  2887. echo shar: Extracting \"'uudecode.c'\" \(9893 characters\)
  2888. sed "s/^X//" >'uudecode.c' <<'END_OF_FILE'
  2889. X/* $Id: uudecode.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
  2890. X *
  2891. X * $Log: uudecode.c,v $
  2892. X * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  2893. X * Patchlevel 2 changes
  2894. X *
  2895. X * Revision 4.4  1991/09/09  20:27:37  sob
  2896. X * release 4.4
  2897. X *
  2898. X * 
  2899. X * Decode one or more uuencoded articles back to binary form.
  2900. X * Adapted to rn 4.4 by Stan Barber
  2901. X * Trn version created by Wayne Davison.
  2902. X * Adapted from the nn version by Kim Storm.
  2903. X * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
  2904. X */
  2905. X/*
  2906. X * This software is Copyright 1991 by Stan Barber. 
  2907. X *
  2908. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  2909. X * use this software as long as: there is no monetary profit gained
  2910. X * specifically from the use or reproduction of this software, it is not
  2911. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  2912. X * included prominently in any copy made. 
  2913. X *
  2914. X * The author make no claims as to the fitness or correctness of this software
  2915. X * for any use whatsoever, and it is provided as is. Any use of this software
  2916. X * is at the user's own risk. 
  2917. X */
  2918. X
  2919. X#include "EXTERN.h"
  2920. X#include "common.h"
  2921. X#include "respond.h"
  2922. X#include "decode.h"
  2923. X
  2924. X#define MAXCHAR 256
  2925. X#define NORMLEN 64    /* allows for 84 encoded chars per line */
  2926. X
  2927. X#define SEQMAX 'z'
  2928. X#define SEQMIN 'a'
  2929. X
  2930. static char seqc;
  2931. static int first, secnd, check, numl;
  2932. X
  2933. static char blank;
  2934. static int chtbl[MAXCHAR], cdlen[NORMLEN + 3];
  2935. static int state;
  2936. static bool Xflag;
  2937. static int expecting_part;
  2938. X
  2939. static int decode_line ANSI((char *));
  2940. static void inittbls ANSI((void));
  2941. static void gettable ANSI((FILE *));
  2942. X
  2943. X#define    NO_ADVANCE        0x10
  2944. X
  2945. X#define    FIND_BEGIN        0x01
  2946. X#define    AFTER_ERROR_FIND_BEGIN    0x02
  2947. X#define    DECODE_TEXT        0x03
  2948. X#define    SKIP_TRAILING           (0x04 | NO_ADVANCE)
  2949. X#define    SKIP_LEADING        0x05
  2950. X#define    FOUND_END           (0x06 | NO_ADVANCE)
  2951. X#define DECODE_ERROR           (0x07 | NO_ADVANCE)
  2952. X#define OTHER_ERROR           (0x08 | NO_ADVANCE)
  2953. X#define NEW_BEGIN           (0x09 | NO_ADVANCE)
  2954. X
  2955. void
  2956. uud_start()
  2957. X{
  2958. X    Xflag = FALSE;
  2959. X    expecting_part = 0;
  2960. X    seqc = SEQMAX;
  2961. X    check = 1;
  2962. X    first = 1;
  2963. X    secnd = 0;
  2964. X    state = FIND_BEGIN;
  2965. X}
  2966. X
  2967. int
  2968. uudecode(in)
  2969. XFILE *in;
  2970. X{
  2971. X    int mode, onedone, lens;
  2972. X    char buff[LBUFLEN];
  2973. X
  2974. X    numl = onedone = 0;
  2975. X
  2976. X    if (state == FIND_BEGIN)
  2977. X    inittbls();
  2978. X
  2979. X    /*
  2980. X     * search for header or translation table line.
  2981. X     */
  2982. X
  2983. X    while ((state & NO_ADVANCE) || fgets(buff, sizeof buff, in) != Nullch) {
  2984. X    numl++;
  2985. X
  2986. X    switch (state) {
  2987. X     case NEW_BEGIN:
  2988. X        if (decode_fp != Nullfp) {
  2989. X        if (expecting_part) {
  2990. X            register int got_part = 0;
  2991. X
  2992. X            if (strnEQ(buff + 6, "part ", 5)) {
  2993. X            register char *bp;
  2994. X
  2995. X            for (bp = buff + 11; islower(*bp); bp++)
  2996. X                got_part = got_part * 26 + *bp - 'a';
  2997. X            }
  2998. X            if (expecting_part == got_part) {
  2999. X            state = DECODE_TEXT;
  3000. X            break;
  3001. X            }
  3002. X            printf("Expecting part %d; got part %d.\n",
  3003. X             expecting_part + 1, got_part + 1);
  3004. X            if (got_part) {
  3005. X            state = SKIP_LEADING;
  3006. X            return -1;
  3007. X            }
  3008. X        }
  3009. X        decode_end();
  3010. X        sleep(2);
  3011. X        Xflag = FALSE;
  3012. X        expecting_part = 0;
  3013. X        }
  3014. X        state = FIND_BEGIN;
  3015. X        /* fall thru */
  3016. X
  3017. X     case FIND_BEGIN:
  3018. X     case AFTER_ERROR_FIND_BEGIN:
  3019. X        if (strnEQ(buff, "table", 5)) {
  3020. X        gettable(in);
  3021. X        continue;
  3022. X        }
  3023. X
  3024. X        if (strnEQ(buff, "begin ", 6)
  3025. X         || strnEQ(buff, "Xbegin ", 7)) {
  3026. X        lens = strlen(buff)-1;
  3027. X        if (buff[lens] == '\n')
  3028. X            buff[lens] = '\0';
  3029. X
  3030. X        if(sscanf(buff+6,"%o%s", &mode, decode_fname) != 2) {
  3031. X            register char *bp = buff + 6;
  3032. X
  3033. X            if (*bp == ' ')
  3034. X            bp++;
  3035. X            if (strnEQ(bp, "part ", 5)) {
  3036. X            register int got_part = 0;
  3037. X
  3038. X            for (bp = bp + 5; islower(*bp); bp++)
  3039. X                got_part = got_part * 26 + *bp - 'a';
  3040. X            printf("Expecting part 1; got part %d.\n",
  3041. X                got_part + 1);
  3042. X            return -1;
  3043. X            }
  3044. X            continue;
  3045. X        }
  3046. X
  3047. X        Xflag = (*buff == 'X');
  3048. X
  3049. X        sprintf(decode_dest, "%s/%s", extractdest, decode_fname);
  3050. X
  3051. X        if ((decode_fp = fopen(decode_dest, FOPEN_WB)) == Nullfp) {
  3052. X            printf("Cannot create file: %s\n", decode_dest);
  3053. X            goto err;
  3054. X        }
  3055. X        chmod(decode_dest, mode);
  3056. X        printf("Decoding: %s\n", decode_fname);
  3057. X        state = DECODE_TEXT;
  3058. X        }
  3059. X        continue;
  3060. X
  3061. X     case SKIP_LEADING:
  3062. X        state = decode_line(buff);
  3063. X        continue;
  3064. X
  3065. X     case DECODE_TEXT:
  3066. X        state = decode_line(buff);
  3067. X        onedone = 1;
  3068. X        continue;
  3069. X
  3070. X     case FOUND_END:
  3071. X        fclose(decode_fp);
  3072. X        decode_fp = Nullfp;
  3073. X        Xflag = FALSE;
  3074. X        expecting_part = 0;
  3075. X        state = FIND_BEGIN;
  3076. X        printf("Done.\n");
  3077. X        continue;
  3078. X
  3079. X     case SKIP_TRAILING:
  3080. X        printf("(Continued)\n");
  3081. X        state = SKIP_LEADING;
  3082. X        return 0;
  3083. X
  3084. X     case DECODE_ERROR:
  3085. X        state = SKIP_TRAILING;
  3086. X        continue;
  3087. X
  3088. X     case OTHER_ERROR:
  3089. X        fclose(decode_fp);
  3090. X        decode_fp = Nullfp;
  3091. X        Xflag = FALSE;
  3092. X        expecting_part = 0;
  3093. X        state = AFTER_ERROR_FIND_BEGIN;
  3094. X        goto err;
  3095. X    }
  3096. X    }
  3097. X
  3098. X    if (onedone) {
  3099. X    if (state == DECODE_TEXT) {
  3100. X        printf("(Continued)\n");
  3101. X        state = SKIP_LEADING;
  3102. X    }
  3103. X    return 0;
  3104. X    }
  3105. X
  3106. X    if (state == AFTER_ERROR_FIND_BEGIN)
  3107. X    return -1;
  3108. X    printf("Couldn't find anything to decode.\n");
  3109. X
  3110. X err:
  3111. X    sleep(2);
  3112. X    return -1;
  3113. X}
  3114. X
  3115. X/*
  3116. X * decode one line and write it out using decode_fp
  3117. X */
  3118. X
  3119. static int
  3120. decode_line(buff)
  3121. char *buff;
  3122. X{
  3123. X    char outl[LBUFLEN];
  3124. X    register char *bp, *ut;
  3125. X    register int *trtbl = chtbl;
  3126. X    register int n;
  3127. X    register int blen;        /* binary length (from decoded file) */
  3128. X    register int rlen;        /* calculated input line length */
  3129. X    register int len;        /* actual input line length */
  3130. X    register int dash;        /* number of '-'s encountered on a line */
  3131. X                /* If it's too high, we reject the line */
  3132. X
  3133. X#   define REJECT(buf,rlen,len) /* Comment for makedepend to     \
  3134. X                ** ignore the backslash above */ \
  3135. X    ((*buf == 'M' && len > rlen + 5) \
  3136. X     || (*buf != 'M' && len != rlen && len != rlen+1) \
  3137. X     || (strnEQ(buf, "BEGIN", 5)) \
  3138. X     || (strnEQ(buf, "END", 3)))
  3139. X
  3140. X    if (Xflag) {
  3141. X    if (*buff == 'X')
  3142. X        buff++;
  3143. X    else
  3144. X        *buff = 'x';    /* force a mis-parse of a non-x'ed line */
  3145. X    }
  3146. X    len = strlen(buff);
  3147. X    if (--len <= 0)
  3148. X    return state;
  3149. X
  3150. X    buff[len] = '\0';
  3151. X
  3152. X    /*
  3153. X     * Get the binary line length.
  3154. X     */
  3155. X    if ((blen = trtbl[buff[0]]) < 0) {
  3156. X    if (state == SKIP_LEADING) {
  3157. X        if (strnEQ(buff, "begin ", 6))
  3158. X        return NEW_BEGIN;
  3159. X
  3160. X        return SKIP_LEADING;
  3161. X    }
  3162. X    /*
  3163. X     * end of uuencoded file ?
  3164. X     */
  3165. X    if (strnEQ(buff, "end", 3))
  3166. X        return FOUND_END;
  3167. X
  3168. X    /*
  3169. X     * end of current file ? : get next one.
  3170. X     */
  3171. X    if (strnEQ(buff, "include ", 8)) {
  3172. X        for (bp = buff + 8; *bp; bp++) {
  3173. X        if (bp[0] == '.' && bp[1] == 'u') {
  3174. X            expecting_part = (bp[2] - 'a') * 26 + bp[3] - 'a';
  3175. X            break;
  3176. X        }
  3177. X        }
  3178. X    }
  3179. X
  3180. X    /*
  3181. X     * trailing garbage
  3182. X     */
  3183. X    return SKIP_TRAILING;
  3184. X    }
  3185. X
  3186. X    rlen = cdlen[blen];
  3187. X    if (state == SKIP_LEADING && REJECT(buff,rlen,len))
  3188. X    return SKIP_LEADING;
  3189. X
  3190. X    /*
  3191. X     * Is it the empty line before the end line ?
  3192. X     */
  3193. X    if (blen == 0)
  3194. X    return state;
  3195. X
  3196. X    if (REJECT(buff,rlen,len))
  3197. X    return SKIP_TRAILING;
  3198. X
  3199. X    /*
  3200. X     * Pad with blanks.
  3201. X     */
  3202. X    for (bp = buff + len, n = rlen - len; --n >= 0; )
  3203. X    *bp++ = blank;
  3204. X
  3205. X    /*
  3206. X     * Verify
  3207. X     */
  3208. X    for (n = rlen, bp = buff, dash = 0; --n >= 0; bp++) {
  3209. X    if (trtbl[*bp] < 0) {
  3210. X        if (state == SKIP_LEADING)
  3211. X        return SKIP_LEADING;
  3212. X        return DECODE_ERROR;
  3213. X    }
  3214. X    if (*bp == '-')
  3215. X        dash++;
  3216. X    }
  3217. X    if (dash * 100 / rlen > 33)        /* more than 1/3 dashes? */
  3218. X    if (state == SKIP_LEADING)
  3219. X        return SKIP_LEADING;    /* -> reject */
  3220. X    else
  3221. X        return SKIP_TRAILING;
  3222. X
  3223. X    /*
  3224. X     * Check for uuencodes that append a 'z' to each line....
  3225. X     */
  3226. X    if (check)
  3227. X    if (secnd) {
  3228. X        secnd = 0;
  3229. X        if (buff[rlen] == SEQMAX)
  3230. X        check = 0;
  3231. X    } else if (first) {
  3232. X        first = 0;
  3233. X        secnd = 1;
  3234. X        if (buff[rlen] != SEQMAX)
  3235. X        check = 0;
  3236. X    }
  3237. X
  3238. X    /*
  3239. X     * There we check.
  3240. X     */
  3241. X    if (check) {
  3242. X    if (buff[rlen] != seqc) {
  3243. X        if (state == SKIP_LEADING)
  3244. X        return SKIP_LEADING;
  3245. X        return DECODE_ERROR;
  3246. X    }
  3247. X
  3248. X    if (--seqc < SEQMIN)
  3249. X        seqc = SEQMAX;
  3250. X    }
  3251. X
  3252. X    /*
  3253. X     * output a group of 3 bytes (4 input characters).
  3254. X     * the input chars are pointed to by p, they are to
  3255. X     * be output to file f. blen is used to tell us not to
  3256. X     * output all of them at the end of the file.
  3257. X     */
  3258. X    ut = outl;
  3259. X    n = blen;
  3260. X    bp = &buff[1];
  3261. X    while (--n >= 0) {
  3262. X    *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
  3263. X    if (n > 0) {
  3264. X        *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
  3265. X        n--;
  3266. X    }
  3267. X    if (n > 0) {
  3268. X        *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
  3269. X        n--;
  3270. X    }
  3271. X    bp += 4;
  3272. X    }
  3273. X    if (fwrite(outl, 1, blen, decode_fp) <= 0) {
  3274. X    printf("Error on writing decoded file\n");
  3275. X    return OTHER_ERROR;
  3276. X    }
  3277. X
  3278. X    return DECODE_TEXT;
  3279. X}
  3280. X
  3281. X
  3282. X
  3283. X/*
  3284. X * Install the table in memory for later use.
  3285. X */
  3286. static void
  3287. inittbls()
  3288. X{
  3289. X    register int i, j;
  3290. X
  3291. X    /*
  3292. X     * Set up the default translation table.
  3293. X     */
  3294. X    for (i = 0; i < ' '; i++)
  3295. X    chtbl[i] = -1;
  3296. X    for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
  3297. X    chtbl[i] = j;
  3298. X    for (i = ' ' + 64; i < MAXCHAR; i++)
  3299. X    chtbl[i] = -1;
  3300. X    chtbl['`'] = chtbl[' '];    /* common mutation */
  3301. X    chtbl['~'] = chtbl['^'];    /* another common mutation */
  3302. X    blank = ' ';
  3303. X    /*
  3304. X     * set up the line length table, to avoid computing lotsa * and / ...
  3305. X     */
  3306. X    cdlen[0] = 1;
  3307. X    for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
  3308. X    cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
  3309. X}
  3310. X
  3311. static void
  3312. gettable(in)
  3313. XFILE *in;
  3314. X{
  3315. X    char buff[LBUFLEN];
  3316. X    register int c, n = 0;
  3317. X    register char *cpt;
  3318. X
  3319. X    for (c = 0; c < MAXCHAR; c++)
  3320. X    chtbl[c] = -1;
  3321. X
  3322. X    for (;;) {
  3323. X    if (fgets(buff, sizeof buff, in) == Nullch) {
  3324. X        printf("EOF while in translation table.\n");
  3325. X        return;
  3326. X    }
  3327. X    numl++;
  3328. X    if (strnEQ(buff, "begin", 5)) {
  3329. X        printf("Incomplete translation table.\n");
  3330. X        return;
  3331. X    }
  3332. X    cpt = buff + strlen(buff) - 1;
  3333. X    *cpt = ' ';
  3334. X    while (*cpt == ' ') {
  3335. X        *cpt = 0;
  3336. X        cpt--;
  3337. X    }
  3338. X    cpt = buff;
  3339. X    while (c = *cpt) {
  3340. X        if (chtbl[c] != -1) {
  3341. X        printf("Duplicate char in translation table.\n");
  3342. X        return;
  3343. X        }
  3344. X        if (n == 0)
  3345. X        blank = c;
  3346. X        chtbl[c] = n++;
  3347. X        if (n >= 64)
  3348. X        return;
  3349. X        cpt++;
  3350. X    }
  3351. X    }
  3352. X}
  3353. X
  3354. END_OF_FILE
  3355. if test 9893 -ne `wc -c <'uudecode.c'`; then
  3356.     echo shar: \"'uudecode.c'\" unpacked with wrong size!
  3357. fi
  3358. # end of 'uudecode.c'
  3359. fi
  3360. echo shar: End of archive 4 \(of 13\).
  3361. cp /dev/null ark4isdone
  3362. MISSING=""
  3363. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
  3364.     if test ! -f ark${I}isdone ; then
  3365.     MISSING="${MISSING} ${I}"
  3366.     fi
  3367. done
  3368. if test "${MISSING}" = "" ; then
  3369.     echo You have unpacked all 13 archives.
  3370.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  3371. else
  3372.     echo You still need to unpack the following archives:
  3373.     echo "        " ${MISSING}
  3374. fi
  3375. ##  End of shell archive.
  3376. exit 0
  3377.